Merge Windows-specific changes for 2.7.0 and 2.7.1 into the main ChangeLog and
[pidgin-git.git] / finch / gntnotify.c
blob63887114c1e9b93e7bece8d53df29d2f93f104b0
1 /**
2 * @file gntnotify.c GNT Notify API
3 * @ingroup finch
4 */
6 /* finch
8 * Finch is the legal property of its developers, whose names are too numerous
9 * to list here. Please refer to the COPYRIGHT file distributed with this
10 * source distribution.
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
26 #include <internal.h>
28 #include <gnt.h>
29 #include <gntbox.h>
30 #include <gntbutton.h>
31 #include <gntlabel.h>
32 #include <gnttree.h>
33 #include <gntutils.h>
34 #include <gntwindow.h>
36 #include "finch.h"
38 #include <util.h>
40 #include "gntnotify.h"
41 #include "debug.h"
43 static struct
45 GntWidget *window;
46 GntWidget *tree;
47 } emaildialog;
49 static void
50 notify_msg_window_destroy_cb(GntWidget *window, PurpleNotifyMsgType type)
52 purple_notify_close(type, window);
55 static void *
56 finch_notify_message(PurpleNotifyMsgType type, const char *title,
57 const char *primary, const char *secondary)
59 GntWidget *window, *button;
60 GntTextFormatFlags pf = 0, sf = 0;
62 switch (type)
64 case PURPLE_NOTIFY_MSG_ERROR:
65 sf |= GNT_TEXT_FLAG_BOLD;
66 case PURPLE_NOTIFY_MSG_WARNING:
67 pf |= GNT_TEXT_FLAG_UNDERLINE;
68 case PURPLE_NOTIFY_MSG_INFO:
69 pf |= GNT_TEXT_FLAG_BOLD;
70 break;
73 window = gnt_window_box_new(FALSE, TRUE);
74 gnt_box_set_title(GNT_BOX(window), title);
75 gnt_box_set_fill(GNT_BOX(window), FALSE);
76 gnt_box_set_alignment(GNT_BOX(window), GNT_ALIGN_MID);
77 gnt_box_set_pad(GNT_BOX(window), 0);
79 if (primary)
80 gnt_box_add_widget(GNT_BOX(window),
81 gnt_label_new_with_format(primary, pf));
83 button = gnt_button_new(_("OK"));
85 if (secondary) {
86 GntWidget *msg;
87 if (type == PURPLE_NOTIFY_FORMATTED) {
88 int width = -1, height = -1;
89 char *plain = (char*)secondary;
90 msg = gnt_text_view_new();
91 gnt_text_view_set_flag(GNT_TEXT_VIEW(msg), GNT_TEXT_VIEW_TOP_ALIGN | GNT_TEXT_VIEW_NO_SCROLL);
92 switch (type) {
93 case PURPLE_NOTIFY_FORMATTED:
94 plain = purple_markup_strip_html(secondary);
95 if (gnt_util_parse_xhtml_to_textview(secondary, GNT_TEXT_VIEW(msg)))
96 break;
97 /* Fallthrough */
98 default:
99 gnt_text_view_append_text_with_flags(GNT_TEXT_VIEW(msg), plain, sf);
101 gnt_text_view_attach_scroll_widget(GNT_TEXT_VIEW(msg), button);
102 gnt_util_get_text_bound(plain, &width, &height);
103 gnt_widget_set_size(msg, width + 3, height + 1);
104 if (plain != secondary)
105 g_free(plain);
106 } else {
107 msg = gnt_label_new_with_format(secondary, sf);
109 gnt_box_add_widget(GNT_BOX(window), msg);
110 g_object_set_data(G_OBJECT(window), "info-widget", msg);
113 gnt_box_add_widget(GNT_BOX(window), button);
114 g_signal_connect_swapped(G_OBJECT(button), "activate",
115 G_CALLBACK(gnt_widget_destroy), window);
116 g_signal_connect(G_OBJECT(window), "destroy",
117 G_CALLBACK(notify_msg_window_destroy_cb), GINT_TO_POINTER(type));
119 gnt_widget_show(window);
120 return window;
123 /* handle is, in all/most occasions, a GntWidget * */
124 static void finch_close_notify(PurpleNotifyType type, void *handle)
126 GntWidget *widget = handle;
128 if (!widget)
129 return;
131 while (widget->parent)
132 widget = widget->parent;
134 if (type == PURPLE_NOTIFY_SEARCHRESULTS)
135 purple_notify_searchresults_free(g_object_get_data(handle, "notify-results"));
136 #if 1
137 /* This did not seem to be necessary */
138 g_signal_handlers_disconnect_by_func(G_OBJECT(widget),
139 G_CALLBACK(notify_msg_window_destroy_cb), GINT_TO_POINTER(type));
140 #endif
141 gnt_widget_destroy(widget);
144 static void *finch_notify_formatted(const char *title, const char *primary,
145 const char *secondary, const char *text)
147 char *xhtml = NULL;
148 char *t = g_strdup_printf("<span>%s%s%s</span>",
149 secondary ? secondary : "",
150 secondary ? "\n" : "",
151 text ? text : "");
152 void *ret;
154 purple_markup_html_to_xhtml(t, &xhtml, NULL);
155 ret = finch_notify_message(PURPLE_NOTIFY_FORMATTED, title, primary, xhtml);
157 g_free(t);
158 g_free(xhtml);
160 return ret;
163 static void
164 reset_email_dialog(void)
166 emaildialog.window = NULL;
167 emaildialog.tree = NULL;
170 static void
171 setup_email_dialog(void)
173 GntWidget *box, *tree, *button;
174 if (emaildialog.window)
175 return;
177 emaildialog.window = box = gnt_vbox_new(FALSE);
178 gnt_box_set_toplevel(GNT_BOX(box), TRUE);
179 gnt_box_set_title(GNT_BOX(box), _("Emails"));
180 gnt_box_set_fill(GNT_BOX(box), FALSE);
181 gnt_box_set_alignment(GNT_BOX(box), GNT_ALIGN_MID);
182 gnt_box_set_pad(GNT_BOX(box), 0);
184 gnt_box_add_widget(GNT_BOX(box),
185 gnt_label_new_with_format(_("You have mail!"), GNT_TEXT_FLAG_BOLD));
187 emaildialog.tree = tree = gnt_tree_new_with_columns(3);
188 gnt_tree_set_column_titles(GNT_TREE(tree), _("Account"), _("Sender"), _("Subject"));
189 gnt_tree_set_show_title(GNT_TREE(tree), TRUE);
190 gnt_tree_set_col_width(GNT_TREE(tree), 0, 15);
191 gnt_tree_set_col_width(GNT_TREE(tree), 1, 25);
192 gnt_tree_set_col_width(GNT_TREE(tree), 2, 25);
194 gnt_box_add_widget(GNT_BOX(box), tree);
196 button = gnt_button_new(_("Close"));
197 gnt_box_add_widget(GNT_BOX(box), button);
199 g_signal_connect_swapped(G_OBJECT(button), "activate", G_CALLBACK(gnt_widget_destroy), box);
200 g_signal_connect(G_OBJECT(box), "destroy", G_CALLBACK(reset_email_dialog), NULL);
203 static void *
204 finch_notify_emails(PurpleConnection *gc, size_t count, gboolean detailed,
205 const char **subjects, const char **froms, const char **tos,
206 const char **urls)
208 PurpleAccount *account = purple_connection_get_account(gc);
209 GString *message = g_string_new(NULL);
210 void *ret;
211 static int key = 0;
213 if (count == 0)
214 return NULL;
216 if (!detailed)
218 g_string_append_printf(message,
219 ngettext("%s (%s) has %d new message.",
220 "%s (%s) has %d new messages.",
221 (int)count),
222 tos ? *tos : purple_account_get_username(account),
223 purple_account_get_protocol_name(account), (int)count);
225 else
227 char *to;
228 gboolean newwin = (emaildialog.window == NULL);
230 if (newwin)
231 setup_email_dialog();
233 to = g_strdup_printf("%s (%s)", tos ? *tos : purple_account_get_username(account),
234 purple_account_get_protocol_name(account));
235 gnt_tree_add_row_after(GNT_TREE(emaildialog.tree), GINT_TO_POINTER(++key),
236 gnt_tree_create_row(GNT_TREE(emaildialog.tree), to,
237 froms ? *froms : "[Unknown sender]",
238 *subjects),
239 NULL, NULL);
240 g_free(to);
241 if (newwin)
242 gnt_widget_show(emaildialog.window);
243 else
244 gnt_window_present(emaildialog.window);
245 return NULL;
248 ret = finch_notify_message(PURPLE_NOTIFY_EMAIL, _("New Mail"), _("You have mail!"), message->str);
249 g_string_free(message, TRUE);
250 return ret;
253 static void *
254 finch_notify_email(PurpleConnection *gc, const char *subject, const char *from,
255 const char *to, const char *url)
257 return finch_notify_emails(gc, 1, subject != NULL,
258 subject ? &subject : NULL,
259 from ? &from : NULL,
260 to ? &to : NULL,
261 url ? &url : NULL);
264 /** User information. **/
265 static GHashTable *userinfo;
267 static char *
268 userinfo_hash(PurpleAccount *account, const char *who)
270 char key[256];
271 g_snprintf(key, sizeof(key), "%s - %s", purple_account_get_username(account), purple_normalize(account, who));
272 return g_utf8_strup(key, -1);
275 static void
276 remove_userinfo(GntWidget *widget, gpointer key)
278 g_hash_table_remove(userinfo, key);
281 static char *
282 purple_notify_user_info_get_xhtml(PurpleNotifyUserInfo *user_info)
284 GList *l;
285 GString *text;
287 text = g_string_new("<span>");
289 for (l = purple_notify_user_info_get_entries(user_info); l != NULL;
290 l = l->next) {
291 PurpleNotifyUserInfoEntry *user_info_entry = l->data;
292 PurpleNotifyUserInfoEntryType type = purple_notify_user_info_entry_get_type(user_info_entry);
293 const char *label = purple_notify_user_info_entry_get_label(user_info_entry);
294 const char *value = purple_notify_user_info_entry_get_value(user_info_entry);
296 /* Handle the label/value pair itself */
297 if (type == PURPLE_NOTIFY_USER_INFO_ENTRY_SECTION_HEADER)
298 g_string_append(text, "<u>");
299 if (label)
300 g_string_append_printf(text, "<b>%s</b>", label);
301 g_string_append(text, "<span>");
302 if (label && value)
303 g_string_append(text, ": ");
304 if (value) {
305 char *strip = purple_markup_strip_html(value);
306 g_string_append(text, strip);
307 g_free(strip);
309 g_string_append(text, "</span>");
310 if (type == PURPLE_NOTIFY_USER_INFO_ENTRY_SECTION_HEADER)
311 g_string_append(text, "</u>");
312 else if (type == PURPLE_NOTIFY_USER_INFO_ENTRY_SECTION_BREAK)
313 g_string_append(text, "<HR/>");
314 g_string_append(text, "<BR/>");
316 g_string_append(text, "</span>");
318 return g_string_free(text, FALSE);
321 static void *
322 finch_notify_userinfo(PurpleConnection *gc, const char *who, PurpleNotifyUserInfo *user_info)
324 char *primary;
325 char *info;
326 void *ui_handle;
327 char *key = userinfo_hash(purple_connection_get_account(gc), who);
329 info = purple_notify_user_info_get_xhtml(user_info);
331 ui_handle = g_hash_table_lookup(userinfo, key);
332 if (ui_handle != NULL) {
333 GntTextView *msg = GNT_TEXT_VIEW(g_object_get_data(G_OBJECT(ui_handle), "info-widget"));
334 char *strip = purple_markup_strip_html(info);
335 int tvw, tvh, width, height, ntvw, ntvh;
337 while (GNT_WIDGET(ui_handle)->parent)
338 ui_handle = GNT_WIDGET(ui_handle)->parent;
339 gnt_widget_get_size(GNT_WIDGET(ui_handle), &width, &height);
340 gnt_widget_get_size(GNT_WIDGET(msg), &tvw, &tvh);
342 gnt_text_view_clear(msg);
343 if (!gnt_util_parse_xhtml_to_textview(info, msg))
344 gnt_text_view_append_text_with_flags(msg, strip, GNT_TEXT_FLAG_NORMAL);
345 gnt_text_view_scroll(msg, 0);
346 gnt_util_get_text_bound(strip, &ntvw, &ntvh);
347 ntvw += 3;
348 ntvh++;
350 gnt_screen_resize_widget(GNT_WIDGET(ui_handle), width + MAX(0, ntvw - tvw), height + MAX(0, ntvh - tvh));
351 g_free(strip);
352 g_free(key);
353 } else {
354 primary = g_strdup_printf(_("Info for %s"), who);
355 ui_handle = finch_notify_formatted(_("Buddy Information"), primary, NULL, info);
356 g_hash_table_insert(userinfo, key, ui_handle);
357 g_free(primary);
358 g_signal_connect(G_OBJECT(ui_handle), "destroy", G_CALLBACK(remove_userinfo), key);
361 g_free(info);
362 return ui_handle;
365 static void
366 notify_button_activated(GntWidget *widget, PurpleNotifySearchButton *b)
368 GList *list = NULL;
369 PurpleAccount *account = g_object_get_data(G_OBJECT(widget), "notify-account");
370 gpointer data = g_object_get_data(G_OBJECT(widget), "notify-data");
372 list = gnt_tree_get_selection_text_list(GNT_TREE(g_object_get_data(G_OBJECT(widget), "notify-tree")));
374 b->callback(purple_account_get_connection(account), list, data);
375 g_list_foreach(list, (GFunc)g_free, NULL);
376 g_list_free(list);
379 static void
380 finch_notify_sr_new_rows(PurpleConnection *gc,
381 PurpleNotifySearchResults *results, void *data)
383 GntTree *tree = GNT_TREE(data);
384 GList *o;
386 /* XXX: Do I need to empty the tree here? */
388 for (o = results->rows; o; o = o->next)
390 gnt_tree_add_row_after(GNT_TREE(tree), o->data,
391 gnt_tree_create_row_from_list(GNT_TREE(tree), o->data),
392 NULL, NULL);
396 static void *
397 finch_notify_searchresults(PurpleConnection *gc, const char *title,
398 const char *primary, const char *secondary,
399 PurpleNotifySearchResults *results, gpointer data)
401 GntWidget *window, *tree, *box, *button;
402 GList *iter;
403 int columns, i;
405 window = gnt_vbox_new(FALSE);
406 gnt_box_set_toplevel(GNT_BOX(window), TRUE);
407 gnt_box_set_title(GNT_BOX(window), title);
408 gnt_box_set_fill(GNT_BOX(window), TRUE);
409 gnt_box_set_pad(GNT_BOX(window), 0);
410 gnt_box_set_alignment(GNT_BOX(window), GNT_ALIGN_MID);
412 if (primary)
413 gnt_box_add_widget(GNT_BOX(window),
414 gnt_label_new_with_format(primary, GNT_TEXT_FLAG_BOLD));
415 if (secondary)
416 gnt_box_add_widget(GNT_BOX(window),
417 gnt_label_new_with_format(secondary, GNT_TEXT_FLAG_NORMAL));
419 columns = g_list_length(results->columns);
420 tree = gnt_tree_new_with_columns(columns);
421 gnt_tree_set_show_title(GNT_TREE(tree), TRUE);
422 gnt_box_add_widget(GNT_BOX(window), tree);
424 i = 0;
425 for (iter = results->columns; iter; iter = iter->next)
427 PurpleNotifySearchColumn *column = iter->data;
428 gnt_tree_set_column_title(GNT_TREE(tree), i, column->title);
429 i++;
432 box = gnt_hbox_new(TRUE);
434 for (iter = results->buttons; iter; iter = iter->next)
436 PurpleNotifySearchButton *b = iter->data;
437 const char *text;
439 switch (b->type)
441 case PURPLE_NOTIFY_BUTTON_LABELED:
442 text = b->label;
443 break;
444 case PURPLE_NOTIFY_BUTTON_CONTINUE:
445 text = _("Continue");
446 break;
447 case PURPLE_NOTIFY_BUTTON_ADD:
448 text = _("Add");
449 break;
450 case PURPLE_NOTIFY_BUTTON_INFO:
451 text = _("Info");
452 break;
453 case PURPLE_NOTIFY_BUTTON_IM:
454 text = _("IM");
455 break;
456 case PURPLE_NOTIFY_BUTTON_JOIN:
457 text = _("Join");
458 break;
459 case PURPLE_NOTIFY_BUTTON_INVITE:
460 text = _("Invite");
461 break;
462 default:
463 text = _("(none)");
466 button = gnt_button_new(text);
467 g_object_set_data(G_OBJECT(button), "notify-account", purple_connection_get_account(gc));
468 g_object_set_data(G_OBJECT(button), "notify-data", data);
469 g_object_set_data(G_OBJECT(button), "notify-tree", tree);
470 g_signal_connect(G_OBJECT(button), "activate",
471 G_CALLBACK(notify_button_activated), b);
473 gnt_box_add_widget(GNT_BOX(box), button);
476 gnt_box_add_widget(GNT_BOX(window), box);
478 finch_notify_sr_new_rows(gc, results, tree);
480 gnt_widget_show(window);
481 g_object_set_data(G_OBJECT(window), "notify-results", results);
483 return tree;
486 static void *
487 finch_notify_uri(const char *url)
489 return finch_notify_message(PURPLE_NOTIFY_URI, _("URI"), url, NULL);
492 static PurpleNotifyUiOps ops =
494 finch_notify_message,
495 finch_notify_email,
496 finch_notify_emails,
497 finch_notify_formatted,
498 finch_notify_searchresults,
499 finch_notify_sr_new_rows,
500 finch_notify_userinfo,
501 finch_notify_uri,
502 finch_close_notify, /* The rest of the notify-uiops return a GntWidget.
503 These widgets should be destroyed from here. */
504 NULL,
505 NULL,
506 NULL,
507 NULL
511 PurpleNotifyUiOps *finch_notify_get_ui_ops()
513 return &ops;
516 void finch_notify_init()
518 userinfo = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
521 void finch_notify_uninit()
523 g_hash_table_destroy(userinfo);