/* purple * * Purple is the legal property of its developers, whose names are too numerous * to list here. Please refer to the COPYRIGHT file distributed with this * source distribution. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA */ #include "notify.h" static PurpleNotifyUiOps *notify_ui_ops = NULL; static GList *handles = NULL; typedef struct { PurpleNotifyType type; void *handle; void *ui_handle; PurpleNotifyCloseCallback cb; gpointer cb_user_data; } PurpleNotifyInfo; /* * Definition of a user info entry */ struct _PurpleNotifyUserInfoEntry { char *label; char *value; PurpleNotifyUserInfoEntryType type; }; struct _PurpleNotifyUserInfo { GQueue entries; }; /* * Single column of a search result. */ struct _PurpleNotifySearchColumn { char *title; /* Title of the column. */ gboolean visible; /* Should the column be visible to the user. Defaults to TRUE. */ }; void * purple_notify_message(void *handle, PurpleNotifyMessageType type, const char *title, const char *primary, const char *secondary, PurpleRequestCommonParameters *cpar, PurpleNotifyCloseCallback cb, gpointer user_data) { PurpleNotifyUiOps *ops; g_return_val_if_fail(primary != NULL, NULL); ops = purple_notify_get_ui_ops(); if (ops != NULL && ops->notify_message != NULL) { void *ui_handle = ops->notify_message(type, title, primary, secondary, cpar); if (ui_handle != NULL) { PurpleNotifyInfo *info = g_new0(PurpleNotifyInfo, 1); info->type = PURPLE_NOTIFY_MESSAGE; info->handle = handle; info->ui_handle = ui_handle; info->cb = cb; info->cb_user_data = user_data; handles = g_list_append(handles, info); return info->ui_handle; } } if (cb != NULL) cb(user_data); return NULL; } void * purple_notify_formatted(void *handle, const char *title, const char *primary, const char *secondary, const char *text, PurpleNotifyCloseCallback cb, gpointer user_data) { PurpleNotifyUiOps *ops; g_return_val_if_fail(primary != NULL, NULL); ops = purple_notify_get_ui_ops(); if (ops != NULL && ops->notify_formatted != NULL) { void *ui_handle = ops->notify_formatted(title, primary, secondary, text); if (ui_handle != NULL) { PurpleNotifyInfo *info = g_new0(PurpleNotifyInfo, 1); info->type = PURPLE_NOTIFY_FORMATTED; info->handle = handle; info->ui_handle = ui_handle; info->cb = cb; info->cb_user_data = user_data; handles = g_list_append(handles, info); return info->ui_handle; } } if (cb != NULL) cb(user_data); return NULL; } void * purple_notify_searchresults(PurpleConnection *gc, const char *title, const char *primary, const char *secondary, PurpleNotifySearchResults *results, PurpleNotifyCloseCallback cb, gpointer user_data) { PurpleNotifyUiOps *ops; ops = purple_notify_get_ui_ops(); if (ops != NULL && ops->notify_searchresults != NULL) { void *ui_handle = ops->notify_searchresults(gc, title, primary, secondary, results, user_data); if (ui_handle != NULL) { PurpleNotifyInfo *info = g_new0(PurpleNotifyInfo, 1); info->type = PURPLE_NOTIFY_SEARCHRESULTS; info->handle = gc; info->ui_handle = ui_handle; info->cb = cb; info->cb_user_data = user_data; handles = g_list_append(handles, info); return info->ui_handle; } } if (cb != NULL) cb(user_data); return NULL; } void purple_notify_searchresults_free(PurpleNotifySearchResults *results) { GList *l; g_return_if_fail(results != NULL); for (l = results->buttons; l; l = g_list_delete_link(l, l)) { PurpleNotifySearchButton *button = l->data; g_free(button->label); g_free(button); } for (l = results->rows; l; l = g_list_delete_link(l, l)) { GList *row = l->data; g_list_free_full(row, g_free); } for (l = results->columns; l; l = g_list_delete_link(l, l)) { PurpleNotifySearchColumn *column = l->data; g_free(column->title); g_free(column); } g_free(results); } void purple_notify_searchresults_new_rows(PurpleConnection *gc, PurpleNotifySearchResults *results, void *data) { PurpleNotifyUiOps *ops; ops = purple_notify_get_ui_ops(); if (ops != NULL && ops->notify_searchresults != NULL) { ops->notify_searchresults_new_rows(gc, results, data); } } void purple_notify_searchresults_button_add(PurpleNotifySearchResults *results, PurpleNotifySearchButtonType type, PurpleNotifySearchResultsCallback cb) { PurpleNotifySearchButton *button; g_return_if_fail(results != NULL); g_return_if_fail(cb != NULL); button = g_new0(PurpleNotifySearchButton, 1); button->callback = cb; button->type = type; results->buttons = g_list_append(results->buttons, button); } void purple_notify_searchresults_button_add_labeled(PurpleNotifySearchResults *results, const char *label, PurpleNotifySearchResultsCallback cb) { PurpleNotifySearchButton *button; g_return_if_fail(results != NULL); g_return_if_fail(cb != NULL); g_return_if_fail(label != NULL); g_return_if_fail(*label != '\0'); button = g_new0(PurpleNotifySearchButton, 1); button->callback = cb; button->type = PURPLE_NOTIFY_BUTTON_LABELED; button->label = g_strdup(label); results->buttons = g_list_append(results->buttons, button); } PurpleNotifySearchResults * purple_notify_searchresults_new(void) { PurpleNotifySearchResults *rs = g_new0(PurpleNotifySearchResults, 1); return rs; } void purple_notify_searchresults_column_add(PurpleNotifySearchResults *results, PurpleNotifySearchColumn *column) { g_return_if_fail(results != NULL); g_return_if_fail(column != NULL); results->columns = g_list_append(results->columns, column); } void purple_notify_searchresults_row_add(PurpleNotifySearchResults *results, GList *row) { g_return_if_fail(results != NULL); g_return_if_fail(row != NULL); results->rows = g_list_append(results->rows, row); } PurpleNotifySearchColumn * purple_notify_searchresults_column_new(const char *title) { PurpleNotifySearchColumn *sc; g_return_val_if_fail(title != NULL, NULL); sc = g_new0(PurpleNotifySearchColumn, 1); sc->title = g_strdup(title); sc->visible = TRUE; return sc; } const char *purple_notify_searchresult_column_get_title(const PurpleNotifySearchColumn *column) { g_return_val_if_fail(column != NULL, NULL); return column->title; } void purple_notify_searchresult_column_set_visible(PurpleNotifySearchColumn *column, gboolean visible) { g_return_if_fail(column != NULL); column->visible = visible; } gboolean purple_notify_searchresult_column_is_visible(const PurpleNotifySearchColumn *column) { g_return_val_if_fail(column != NULL, FALSE); return column->visible; } void * purple_notify_userinfo(PurpleConnection *gc, const char *who, PurpleNotifyUserInfo *user_info, PurpleNotifyCloseCallback cb, gpointer user_data) { PurpleNotifyUiOps *ops; g_return_val_if_fail(who != NULL, NULL); ops = purple_notify_get_ui_ops(); if (ops != NULL && ops->notify_userinfo != NULL) { void *ui_handle; ui_handle = ops->notify_userinfo(gc, who, user_info); if (ui_handle != NULL) { PurpleNotifyInfo *info = g_new0(PurpleNotifyInfo, 1); info->type = PURPLE_NOTIFY_USERINFO; info->handle = gc; info->ui_handle = ui_handle; info->cb = cb; info->cb_user_data = user_data; handles = g_list_append(handles, info); return info->ui_handle; } } if (cb != NULL) cb(user_data); return NULL; } PurpleNotifyUserInfoEntry * purple_notify_user_info_entry_new(const char *label, const char *value) { PurpleNotifyUserInfoEntry *user_info_entry; user_info_entry = g_new0(PurpleNotifyUserInfoEntry, 1); user_info_entry->label = g_strdup(label); user_info_entry->value = g_strdup(value); user_info_entry->type = PURPLE_NOTIFY_USER_INFO_ENTRY_PAIR; return user_info_entry; } void purple_notify_user_info_entry_destroy(PurpleNotifyUserInfoEntry *user_info_entry) { g_return_if_fail(user_info_entry != NULL); g_free(user_info_entry->label); g_free(user_info_entry->value); g_free(user_info_entry); } PurpleNotifyUserInfo * purple_notify_user_info_new(void) { PurpleNotifyUserInfo *user_info; user_info = g_new0(PurpleNotifyUserInfo, 1); g_queue_init(&user_info->entries); return user_info; } void purple_notify_user_info_destroy(PurpleNotifyUserInfo *user_info) { GList *l; for (l = user_info->entries.head; l != NULL; l = l->next) { PurpleNotifyUserInfoEntry *user_info_entry = l->data; purple_notify_user_info_entry_destroy(user_info_entry); } g_queue_clear(&user_info->entries); g_free(user_info); } GQueue * purple_notify_user_info_get_entries(PurpleNotifyUserInfo *user_info) { g_return_val_if_fail(user_info != NULL, NULL); return &user_info->entries; } char * purple_notify_user_info_get_text_with_newline(PurpleNotifyUserInfo *user_info, const char *newline) { GList *l; GString *text; text = g_string_new(""); for (l = user_info->entries.head; l != NULL; l = l->next) { PurpleNotifyUserInfoEntry *user_info_entry = l->data; /* Add a newline before a section header */ if (user_info_entry->type == PURPLE_NOTIFY_USER_INFO_ENTRY_SECTION_HEADER) g_string_append(text, newline); /* Handle the label/value pair itself */ /* XXX Todo: Use a larger size for a section header? */ if (user_info_entry->label) g_string_append_printf(text, "%s", user_info_entry->label); if (user_info_entry->label && user_info_entry->value) g_string_append(text, ": "); if (user_info_entry->value) g_string_append(text, user_info_entry->value); /* Display a section break as a horizontal line */ if (user_info_entry->type == PURPLE_NOTIFY_USER_INFO_ENTRY_SECTION_BREAK) g_string_append(text, "
"); /* Don't insert a new line before or after a section break;
does that for us */ if ((user_info_entry->type != PURPLE_NOTIFY_USER_INFO_ENTRY_SECTION_BREAK) && (l->next && ((((PurpleNotifyUserInfoEntry *)(l->next->data))->type != PURPLE_NOTIFY_USER_INFO_ENTRY_SECTION_BREAK)))) g_string_append(text, newline); /* Add an extra newline after a section header */ if (user_info_entry->type == PURPLE_NOTIFY_USER_INFO_ENTRY_SECTION_HEADER) g_string_append(text, newline); } return g_string_free(text, FALSE); } const gchar * purple_notify_user_info_entry_get_label(PurpleNotifyUserInfoEntry *user_info_entry) { g_return_val_if_fail(user_info_entry != NULL, NULL); return user_info_entry->label; } void purple_notify_user_info_entry_set_label(PurpleNotifyUserInfoEntry *user_info_entry, const char *label) { g_return_if_fail(user_info_entry != NULL); g_free(user_info_entry->label); user_info_entry->label = g_strdup(label); } const gchar * purple_notify_user_info_entry_get_value(PurpleNotifyUserInfoEntry *user_info_entry) { g_return_val_if_fail(user_info_entry != NULL, NULL); return user_info_entry->value; } void purple_notify_user_info_entry_set_value(PurpleNotifyUserInfoEntry *user_info_entry, const char *value) { g_return_if_fail(user_info_entry != NULL); g_free(user_info_entry->value); user_info_entry->value = g_strdup(value); } PurpleNotifyUserInfoEntryType purple_notify_user_info_entry_get_entry_type(PurpleNotifyUserInfoEntry *user_info_entry) { g_return_val_if_fail(user_info_entry != NULL, PURPLE_NOTIFY_USER_INFO_ENTRY_PAIR); return user_info_entry->type; } void purple_notify_user_info_entry_set_entry_type(PurpleNotifyUserInfoEntry *user_info_entry, PurpleNotifyUserInfoEntryType type) { g_return_if_fail(user_info_entry != NULL); user_info_entry->type = type; } void purple_notify_user_info_add_pair_html(PurpleNotifyUserInfo *user_info, const char *label, const char *value) { PurpleNotifyUserInfoEntry *entry; entry = purple_notify_user_info_entry_new(label, value); g_queue_push_tail(&user_info->entries, entry); } void purple_notify_user_info_add_pair_plaintext(PurpleNotifyUserInfo *user_info, const char *label, const char *value) { gchar *escaped; escaped = g_markup_escape_text(value, -1); purple_notify_user_info_add_pair_html(user_info, label, escaped); g_free(escaped); } void purple_notify_user_info_prepend_pair_html(PurpleNotifyUserInfo *user_info, const char *label, const char *value) { PurpleNotifyUserInfoEntry *entry; entry = purple_notify_user_info_entry_new(label, value); g_queue_push_head(&user_info->entries, entry); } void purple_notify_user_info_prepend_pair_plaintext(PurpleNotifyUserInfo *user_info, const char *label, const char *value) { gchar *escaped; escaped = g_markup_escape_text(value, -1); purple_notify_user_info_prepend_pair_html(user_info, label, escaped); g_free(escaped); } void purple_notify_user_info_remove_entry(PurpleNotifyUserInfo *user_info, PurpleNotifyUserInfoEntry *entry) { g_return_if_fail(user_info != NULL); g_return_if_fail(entry != NULL); g_queue_remove(&user_info->entries, entry); } void purple_notify_user_info_add_section_header(PurpleNotifyUserInfo *user_info, const char *label) { PurpleNotifyUserInfoEntry *entry; entry = purple_notify_user_info_entry_new(label, NULL); entry->type = PURPLE_NOTIFY_USER_INFO_ENTRY_SECTION_HEADER; g_queue_push_tail(&user_info->entries, entry); } void purple_notify_user_info_prepend_section_header(PurpleNotifyUserInfo *user_info, const char *label) { PurpleNotifyUserInfoEntry *entry; entry = purple_notify_user_info_entry_new(label, NULL); entry->type = PURPLE_NOTIFY_USER_INFO_ENTRY_SECTION_HEADER; g_queue_push_head(&user_info->entries, entry); } void purple_notify_user_info_add_section_break(PurpleNotifyUserInfo *user_info) { PurpleNotifyUserInfoEntry *entry; entry = purple_notify_user_info_entry_new(NULL, NULL); entry->type = PURPLE_NOTIFY_USER_INFO_ENTRY_SECTION_BREAK; g_queue_push_tail(&user_info->entries, entry); } void purple_notify_user_info_prepend_section_break(PurpleNotifyUserInfo *user_info) { PurpleNotifyUserInfoEntry *entry; entry = purple_notify_user_info_entry_new(NULL, NULL); entry->type = PURPLE_NOTIFY_USER_INFO_ENTRY_SECTION_BREAK; g_queue_push_head(&user_info->entries, entry); } void purple_notify_user_info_remove_last_item(PurpleNotifyUserInfo *user_info) { PurpleNotifyUserInfoEntry *entry; entry = g_queue_pop_tail(&user_info->entries); if (entry) purple_notify_user_info_entry_destroy(entry); } static PurpleNotifyUserInfo * purple_notify_user_info_copy(PurpleNotifyUserInfo *user_info) { PurpleNotifyUserInfo *user_info_copy; GList *l; g_return_val_if_fail(user_info != NULL, NULL); user_info_copy = purple_notify_user_info_new(); for (l = user_info->entries.head; l != NULL; l = l->next) { PurpleNotifyUserInfoEntry *new_entry, *user_info_entry = l->data; new_entry = purple_notify_user_info_entry_new(user_info_entry->label, user_info_entry->value); new_entry->type = user_info_entry->type; g_queue_push_tail(&user_info_copy->entries, new_entry); } return user_info_copy; } GType purple_notify_user_info_get_type(void) { static GType type = 0; if (type == 0) { type = g_boxed_type_register_static("PurpleNotifyUserInfo", (GBoxedCopyFunc)purple_notify_user_info_copy, (GBoxedFreeFunc)purple_notify_user_info_destroy); } return type; } void * purple_notify_uri(void *handle, const char *uri) { PurpleNotifyUiOps *ops; g_return_val_if_fail(uri != NULL, NULL); ops = purple_notify_get_ui_ops(); if (ops != NULL && ops->notify_uri != NULL) { void *ui_handle = ops->notify_uri(uri); if (ui_handle != NULL) { PurpleNotifyInfo *info = g_new0(PurpleNotifyInfo, 1); info->type = PURPLE_NOTIFY_URI; info->handle = handle; info->ui_handle = ui_handle; handles = g_list_append(handles, info); return info->ui_handle; } } return NULL; } gboolean purple_notify_is_valid_ui_handle(void *ui_handle, PurpleNotifyType *type) { GList *it; if (ui_handle == NULL) return FALSE; for (it = handles; it != NULL; it = g_list_next(it)) { PurpleNotifyInfo *info = it->data; if (info->ui_handle != ui_handle) continue; if (type != NULL) *type = info->type; return TRUE; } return FALSE; } void purple_notify_close(G_GNUC_UNUSED PurpleNotifyType type, void *ui_handle) { GList *l; PurpleNotifyUiOps *ops; g_return_if_fail(ui_handle != NULL); ops = purple_notify_get_ui_ops(); for (l = handles; l != NULL; l = l->next) { PurpleNotifyInfo *info = l->data; if (info->ui_handle == ui_handle) { handles = g_list_delete_link(handles, l); if (ops != NULL && ops->close_notify != NULL) ops->close_notify(info->type, ui_handle); if (info->cb != NULL) info->cb(info->cb_user_data); g_free(info); break; } } } void purple_notify_close_with_handle(void *handle) { GList *l, *prev = NULL; PurpleNotifyUiOps *ops; g_return_if_fail(handle != NULL); ops = purple_notify_get_ui_ops(); for (l = handles; l != NULL; l = prev ? prev->next : handles) { PurpleNotifyInfo *info = l->data; if (info->handle == handle) { handles = g_list_delete_link(handles, l); if (ops != NULL && ops->close_notify != NULL) ops->close_notify(info->type, info->ui_handle); if (info->cb != NULL) info->cb(info->cb_user_data); g_free(info); } else prev = l; } } static PurpleNotifyUiOps * purple_notify_ui_ops_copy(PurpleNotifyUiOps *ops) { PurpleNotifyUiOps *ops_new; g_return_val_if_fail(ops != NULL, NULL); ops_new = g_new(PurpleNotifyUiOps, 1); *ops_new = *ops; return ops_new; } GType purple_notify_ui_ops_get_type(void) { static GType type = 0; if (type == 0) { type = g_boxed_type_register_static("PurpleNotifyUiOps", (GBoxedCopyFunc)purple_notify_ui_ops_copy, (GBoxedFreeFunc)g_free); } return type; } static PurpleNotifySearchButton * purple_notify_search_button_copy(PurpleNotifySearchButton *button) { PurpleNotifySearchButton *button_new; g_return_val_if_fail(button != NULL, NULL); button_new = g_new(PurpleNotifySearchButton, 1); *button_new = *button; return button_new; } GType purple_notify_search_button_get_type(void) { static GType type = 0; if (type == 0) { type = g_boxed_type_register_static("PurpleNotifySearchButton", (GBoxedCopyFunc)purple_notify_search_button_copy, (GBoxedFreeFunc)g_free); } return type; } void purple_notify_set_ui_ops(PurpleNotifyUiOps *ops) { notify_ui_ops = ops; } PurpleNotifyUiOps * purple_notify_get_ui_ops(void) { return notify_ui_ops; } void purple_notify_init(void) { } void purple_notify_uninit(void) { }