pidgin/gtknotify.c

changeset 41229
3cd912162649
parent 41097
e0b11d28b39e
child 41231
4fd76e8dedf8
equal deleted inserted replaced
41228:cfabfc1afd50 41229:3cd912162649
38 } PidginUserInfo; 38 } PidginUserInfo;
39 39
40 typedef struct 40 typedef struct
41 { 41 {
42 PurpleAccount *account; 42 PurpleAccount *account;
43 char *url;
44 GtkWidget *label;
45 int count;
46 gboolean purple_has_handle;
47 } PidginNotifyMailData;
48
49 typedef struct
50 {
51 PurpleAccount *account;
52 GtkListStore *model; 43 GtkListStore *model;
53 GtkWidget *treeview; 44 GtkWidget *treeview;
54 GtkWidget *window; 45 GtkWidget *window;
55 gpointer user_data; 46 gpointer user_data;
56 PurpleNotifySearchResults *results; 47 PurpleNotifySearchResults *results;
62 PurpleNotifySearchButton *button; 53 PurpleNotifySearchButton *button;
63 PidginNotifySearchResultsData *data; 54 PidginNotifySearchResultsData *data;
64 55
65 } PidginNotifySearchResultsButtonData; 56 } PidginNotifySearchResultsButtonData;
66 57
67 enum
68 {
69 PIDGIN_MAIL_ICON,
70 PIDGIN_MAIL_TEXT,
71 PIDGIN_MAIL_DATA,
72 COLUMNS_PIDGIN_MAIL
73 };
74
75 typedef struct
76 {
77 /*
78 * This must be first so PidginNotifyDialog can masquerade as the
79 * dialog widget.
80 */
81 GtkWidget *dialog;
82 GtkWidget *treeview;
83 GtkTreeStore *treemodel;
84 GtkLabel *label;
85 GtkWidget *open_button;
86 GtkWidget *dismiss_button;
87 GtkWidget *edit_button;
88 int total_count;
89 gboolean in_use;
90 } PidginNotifyDialog;
91
92 typedef enum
93 {
94 PIDGIN_NOTIFY_MAIL,
95 PIDGIN_NOTIFY_TYPES
96 } PidginNotifyType;
97
98 static PidginNotifyDialog *mail_dialog = NULL;
99
100 static PidginNotifyDialog *pidgin_create_notification_dialog(PidginNotifyType type);
101 static void *pidgin_notify_emails(PurpleConnection *gc, size_t count, gboolean detailed,
102 const char **subjects,
103 const char **froms, const char **tos,
104 const char **urls);
105
106 static void pidgin_close_notify(PurpleNotifyType type, void *ui_handle); 58 static void pidgin_close_notify(PurpleNotifyType type, void *ui_handle);
107 59
108 static void 60 static void
109 message_response_cb(GtkDialog *dialog, gint id, GtkWidget *widget) 61 message_response_cb(GtkDialog *dialog, gint id, GtkWidget *widget)
110 { 62 {
111 purple_notify_close(PURPLE_NOTIFY_MESSAGE, widget); 63 purple_notify_close(PURPLE_NOTIFY_MESSAGE, widget);
112 }
113
114 static void
115 reset_mail_dialog(gpointer unused)
116 {
117 g_return_if_fail(mail_dialog != NULL);
118
119 if (mail_dialog->in_use)
120 return;
121 gtk_widget_destroy(mail_dialog->dialog);
122 g_free(mail_dialog);
123 mail_dialog = NULL;
124 purple_signal_emit(purple_notify_get_handle(), "displaying-emails-clear");
125 }
126
127 gboolean
128 pidgin_notify_emails_pending()
129 {
130 return mail_dialog != NULL
131 && !gtk_widget_get_visible(mail_dialog->dialog);
132 }
133
134 void pidgin_notify_emails_present(void *data)
135 {
136 if (pidgin_notify_emails_pending()) {
137 gtk_widget_show_all(mail_dialog->dialog);
138 mail_dialog->in_use = TRUE;
139 pidgin_blist_set_headline(NULL, NULL, NULL, NULL, NULL);
140 mail_dialog->in_use = FALSE;
141 }
142 purple_signal_emit(purple_notify_get_handle(), "displaying-emails-clear");
143 }
144
145 static void
146 email_response_cb(GtkDialog *unused, gint id, PidginNotifyDialog *unused2)
147 {
148 PidginNotifyMailData *data = NULL;
149 GtkTreeModel *model = GTK_TREE_MODEL(mail_dialog->treemodel);
150 GtkTreeIter iter;
151
152 if (id == GTK_RESPONSE_YES)
153 {
154 /* A single row activated. Remove that row. */
155 GtkTreeSelection *selection;
156
157 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(mail_dialog->treeview));
158
159 if (gtk_tree_selection_get_selected(selection, NULL, &iter))
160 {
161 gtk_tree_model_get(model, &iter, PIDGIN_MAIL_DATA, &data, -1);
162 purple_notify_uri(NULL, data->url);
163
164 gtk_tree_store_remove(mail_dialog->treemodel, &iter);
165 if (data->purple_has_handle)
166 purple_notify_close(PURPLE_NOTIFY_EMAILS, data);
167 else
168 pidgin_close_notify(PURPLE_NOTIFY_EMAILS, data);
169
170 if (gtk_tree_model_get_iter_first(model, &iter))
171 return;
172 }
173 else
174 return;
175 }
176 else
177 {
178 /* Remove all the rows */
179 while (gtk_tree_model_get_iter_first(model, &iter))
180 {
181 gtk_tree_model_get(model, &iter, PIDGIN_MAIL_DATA, &data, -1);
182
183 if (id == GTK_RESPONSE_ACCEPT)
184 purple_notify_uri(NULL, data->url);
185
186 gtk_tree_store_remove(mail_dialog->treemodel, &iter);
187 if (data->purple_has_handle)
188 purple_notify_close(PURPLE_NOTIFY_EMAILS, data);
189 else
190 pidgin_close_notify(PURPLE_NOTIFY_EMAILS, data);
191 }
192 }
193
194 reset_mail_dialog(NULL);
195 }
196
197 static void
198 email_row_activated_cb(GtkTreeView *tv, GtkTreePath *path,
199 GtkTreeViewColumn *col, gpointer data)
200 {
201 email_response_cb(NULL, GTK_RESPONSE_YES, NULL);
202 } 64 }
203 65
204 static gboolean 66 static gboolean
205 formatted_close_cb(GtkWidget *win, GdkEvent *event, void *user_data) 67 formatted_close_cb(GtkWidget *win, GdkEvent *event, void *user_data)
206 { 68 {
359 pidgin_auto_parent_window(dialog); 221 pidgin_auto_parent_window(dialog);
360 222
361 gtk_widget_show_all(dialog); 223 gtk_widget_show_all(dialog);
362 224
363 return dialog; 225 return dialog;
364 }
365
366 static void
367 selection_changed_cb(GtkTreeSelection *sel, PidginNotifyDialog *dialog)
368 {
369 GtkTreeIter iter;
370 GtkTreeModel *model;
371 PidginNotifyMailData *data;
372 gboolean active = TRUE;
373
374 if (gtk_tree_selection_get_selected(sel, &model, &iter) == FALSE)
375 active = FALSE;
376 else
377 {
378 gtk_tree_model_get(model, &iter, PIDGIN_MAIL_DATA, &data, -1);
379 if (data->url == NULL)
380 active = FALSE;
381 }
382
383 gtk_widget_set_sensitive(dialog->open_button, active);
384 }
385
386 static void *
387 pidgin_notify_email(PurpleConnection *gc, const char *subject, const char *from,
388 const char *to, const char *url)
389 {
390 return pidgin_notify_emails(gc, 1, (subject != NULL),
391 (subject == NULL ? NULL : &subject),
392 (from == NULL ? NULL : &from),
393 (to == NULL ? NULL : &to),
394 (url == NULL ? NULL : &url));
395 }
396
397 static int
398 mail_window_focus_cb(GtkWidget *widget, GdkEventFocus *focus, gpointer null)
399 {
400 gtk_window_set_urgency_hint(GTK_WINDOW(widget), FALSE);
401 return 0;
402 }
403
404 /* count == 0 means this is a detailed mail notification.
405 * count > 0 mean non-detailed.
406 */
407 static void *
408 pidgin_notify_add_mail(GtkTreeStore *treemodel, PurpleAccount *account, char *notification, const char *url, int count, gboolean clear, gboolean *new_data)
409 {
410 PidginNotifyMailData *data = NULL;
411 GtkTreeIter iter;
412 GdkPixbuf *icon;
413 gboolean new_n = TRUE;
414
415 if (count > 0 || clear) {
416 /* Allow only one non-detailed email notification for each account */
417 if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(treemodel), &iter)) {
418 gboolean advanced;
419 do {
420 advanced = FALSE;
421 gtk_tree_model_get(GTK_TREE_MODEL(treemodel), &iter,
422 PIDGIN_MAIL_DATA, &data, -1);
423 if (data && data->account == account) {
424 if (clear) {
425 advanced = gtk_tree_store_remove(treemodel, &iter);
426 mail_dialog->total_count -= data->count;
427
428 if (data->purple_has_handle)
429 purple_notify_close(PURPLE_NOTIFY_EMAILS, data);
430 else
431 pidgin_close_notify(PURPLE_NOTIFY_EMAILS, data);
432 /* We're completely done if we've processed all entries */
433 if (!advanced)
434 return NULL;
435 } else if (data->count > 0) {
436 new_n = FALSE;
437 g_free(data->url);
438 data->url = NULL;
439 mail_dialog->total_count -= data->count;
440 break;
441 }
442 }
443 } while (advanced || gtk_tree_model_iter_next(GTK_TREE_MODEL(treemodel), &iter));
444 }
445 }
446
447 if (clear)
448 return NULL;
449
450 icon = pidgin_create_protocol_icon(account, PIDGIN_PROTOCOL_ICON_MEDIUM);
451
452 if (new_n) {
453 data = g_new0(PidginNotifyMailData, 1);
454 data->purple_has_handle = TRUE;
455 gtk_tree_store_append(treemodel, &iter, NULL);
456 }
457
458 if (url != NULL)
459 data->url = g_strdup(url);
460
461 gtk_tree_store_set(treemodel, &iter,
462 PIDGIN_MAIL_ICON, icon,
463 PIDGIN_MAIL_TEXT, notification,
464 PIDGIN_MAIL_DATA, data,
465 -1);
466 data->account = account;
467 /* count == 0 indicates we're adding a single detailed e-mail */
468 data->count = count > 0 ? count : 1;
469
470 if (icon)
471 g_object_unref(icon);
472
473 if (new_data)
474 *new_data = new_n;
475 return data;
476 }
477
478 static void *
479 pidgin_notify_emails(PurpleConnection *gc, size_t count, gboolean detailed,
480 const char **subjects, const char **froms,
481 const char **tos, const char **urls)
482 {
483 char *notification;
484 PurpleAccount *account;
485 PidginNotifyMailData *data = NULL, *data2;
486 gboolean new_data = FALSE;
487 GtkTreeSelection *sel;
488 GtkTreeIter iter;
489
490 /* Don't bother updating if there aren't new emails and we don't have any displayed currently */
491 if (count == 0 && mail_dialog == NULL)
492 return NULL;
493
494 account = purple_connection_get_account(gc);
495 if (mail_dialog == NULL)
496 mail_dialog = pidgin_create_notification_dialog(PIDGIN_NOTIFY_MAIL);
497
498 mail_dialog->total_count += count;
499 if (detailed) {
500 for ( ; count; --count) {
501 char *to_text = NULL;
502 char *from_text = NULL;
503 char *subject_text = NULL;
504 char *tmp;
505 gboolean first = TRUE;
506
507 if (tos != NULL) {
508 tmp = g_markup_escape_text(*tos, -1);
509 to_text = g_strdup_printf("<b>%s</b>: %s\n", _("Account"), tmp);
510 g_free(tmp);
511 first = FALSE;
512 tos++;
513 }
514 if (froms != NULL) {
515 tmp = g_markup_escape_text(*froms, -1);
516 from_text = g_strdup_printf("%s<b>%s</b>: %s\n", first ? "<br>" : "", _("Sender"), tmp);
517 g_free(tmp);
518 first = FALSE;
519 froms++;
520 }
521 if (subjects != NULL) {
522 tmp = g_markup_escape_text(*subjects, -1);
523 subject_text = g_strdup_printf("%s<b>%s</b>: %s", first ? "<br>" : "", _("Subject"), tmp);
524 g_free(tmp);
525 first = FALSE;
526 subjects++;
527 }
528 #define SAFE(x) ((x) ? (x) : "")
529 notification = g_strdup_printf("%s%s%s", SAFE(to_text), SAFE(from_text), SAFE(subject_text));
530 #undef SAFE
531 g_free(to_text);
532 g_free(from_text);
533 g_free(subject_text);
534 (void)first;
535
536 /* If we don't keep track of this, will leak "data" for each of the notifications except the last */
537 data2 = pidgin_notify_add_mail(mail_dialog->treemodel, account, notification, urls ? *urls : NULL, 0, FALSE, &new_data);
538 if (data2 && new_data) {
539 if (data)
540 data->purple_has_handle = FALSE;
541 data = data2;
542 }
543 g_free(notification);
544
545 if (urls != NULL)
546 urls++;
547 }
548 } else {
549 if (count > 0) {
550 notification = g_strdup_printf(
551 ngettext("%s has %d new message.",
552 "%s has %d new messages.", (int)count),
553 tos ? *tos : "(unknown)", (int)count);
554 data2 = pidgin_notify_add_mail(mail_dialog->treemodel, account, notification, urls ? *urls : NULL, count, FALSE, &new_data);
555 if (data2 && new_data) {
556 data = data2;
557 }
558 g_free(notification);
559 } else {
560 /* Clear out all mails for the account */
561 pidgin_notify_add_mail(mail_dialog->treemodel, account, NULL, NULL, 0, TRUE, NULL);
562
563 if (mail_dialog->total_count == 0) {
564 /*
565 * There is no API to clear the headline specifically
566 * This will trigger reset_mail_dialog()
567 */
568 pidgin_blist_set_headline(NULL, NULL, NULL, NULL, NULL);
569 return NULL;
570 }
571 }
572 }
573
574 /* Select first item if nothing selected */
575 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(mail_dialog->treeview));
576 if ((gtk_tree_selection_count_selected_rows(sel) < 1)
577 && gtk_tree_model_get_iter_first(GTK_TREE_MODEL(mail_dialog->treemodel), &iter)) {
578 gtk_tree_selection_select_iter(sel, &iter);
579 }
580
581 if (!gtk_widget_get_visible(mail_dialog->dialog)) {
582 char *label_text = g_strdup_printf(ngettext("<b>%d new email.</b>",
583 "<b>%d new emails.</b>",
584 mail_dialog->total_count), mail_dialog->total_count);
585 mail_dialog->in_use = TRUE; /* So that _set_headline doesn't accidentally
586 remove the notifications when replacing an
587 old notification. */
588 pidgin_blist_set_headline(label_text, "mail-unread",
589 G_CALLBACK(pidgin_notify_emails_present),
590 mail_dialog->dialog,
591 reset_mail_dialog);
592 mail_dialog->in_use = FALSE;
593 g_free(label_text);
594 } else if(!gtk_widget_has_focus(mail_dialog->dialog)) {
595 gtk_window_set_urgency_hint(GTK_WINDOW(mail_dialog->dialog), TRUE);
596 }
597
598 return data;
599 } 226 }
600 227
601 static gboolean 228 static gboolean
602 formatted_input_cb(GtkWidget *win, GdkEventKey *event, gpointer data) 229 formatted_input_cb(GtkWidget *win, GdkEventKey *event, gpointer data)
603 { 230 {
962 } 589 }
963 590
964 static void 591 static void
965 pidgin_close_notify(PurpleNotifyType type, void *ui_handle) 592 pidgin_close_notify(PurpleNotifyType type, void *ui_handle)
966 { 593 {
967 if (type == PURPLE_NOTIFY_EMAIL || type == PURPLE_NOTIFY_EMAILS) 594 if (type == PURPLE_NOTIFY_SEARCHRESULTS)
968 {
969 PidginNotifyMailData *data = (PidginNotifyMailData *)ui_handle;
970
971 if (data) {
972 g_free(data->url);
973 g_free(data);
974 }
975 }
976 else if (type == PURPLE_NOTIFY_SEARCHRESULTS)
977 { 595 {
978 PidginNotifySearchResultsData *data = (PidginNotifySearchResultsData *)ui_handle; 596 PidginNotifySearchResultsData *data = (PidginNotifySearchResultsData *)ui_handle;
979 597
980 gtk_widget_destroy(data->window); 598 gtk_widget_destroy(data->window);
981 purple_notify_searchresults_free(data->results); 599 purple_notify_searchresults_free(data->results);
991 gtk_show_uri_on_window(NULL, uri, GDK_CURRENT_TIME, NULL); 609 gtk_show_uri_on_window(NULL, uri, GDK_CURRENT_TIME, NULL);
992 610
993 return NULL; 611 return NULL;
994 } 612 }
995 613
996 static PidginNotifyDialog *
997 pidgin_create_notification_dialog(PidginNotifyType type)
998 {
999 GtkTreeStore *model = NULL;
1000 GtkWidget *dialog = NULL;
1001 GtkWidget *label = NULL;
1002 GtkCellRenderer *rend;
1003 GtkTreeViewColumn *column;
1004 GtkWidget *button = NULL;
1005 GtkWidget *vbox = NULL;
1006 GtkTreeSelection *sel;
1007 PidginNotifyDialog *spec_dialog = NULL;
1008
1009 g_return_val_if_fail(type < PIDGIN_NOTIFY_TYPES, NULL);
1010
1011 if (type == PIDGIN_NOTIFY_MAIL) {
1012 g_return_val_if_fail(mail_dialog == NULL, mail_dialog);
1013
1014 model = gtk_tree_store_new(COLUMNS_PIDGIN_MAIL,
1015 GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_POINTER);
1016
1017 }
1018
1019 dialog = gtk_dialog_new();
1020
1021 /* Vertical box */
1022 vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
1023
1024 /* Setup the dialog */
1025 gtk_container_set_border_width(GTK_CONTAINER(dialog), 6);
1026 gtk_container_set_border_width(GTK_CONTAINER(vbox), 6);
1027 gtk_box_set_spacing(GTK_BOX(vbox), 12);
1028
1029 /* Golden ratio it up! */
1030 gtk_widget_set_size_request(dialog, 550, 400);
1031
1032 spec_dialog = g_new0(PidginNotifyDialog, 1);
1033 spec_dialog->dialog = dialog;
1034
1035 spec_dialog->treemodel = model;
1036 spec_dialog->treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
1037 g_object_unref(G_OBJECT(model));
1038
1039 if (type == PIDGIN_NOTIFY_MAIL) {
1040 gtk_window_set_title(GTK_WINDOW(dialog), _("New Mail"));
1041 gtk_window_set_role(GTK_WINDOW(dialog), "new_mail_detailed");
1042 g_signal_connect(G_OBJECT(dialog), "focus-in-event",
1043 G_CALLBACK(mail_window_focus_cb), NULL);
1044
1045 gtk_dialog_add_button(GTK_DIALOG(dialog),
1046 _("Open All Messages"), GTK_RESPONSE_ACCEPT);
1047
1048 button = gtk_dialog_add_button(GTK_DIALOG(dialog), _("_Open Mail"),
1049 GTK_RESPONSE_YES);
1050 spec_dialog->open_button = button;
1051
1052 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(spec_dialog->treeview), FALSE);
1053
1054 gtk_tree_view_set_search_column(GTK_TREE_VIEW(spec_dialog->treeview), PIDGIN_MAIL_TEXT);
1055 gtk_tree_view_set_search_equal_func(GTK_TREE_VIEW(spec_dialog->treeview),
1056 pidgin_tree_view_search_equal_func, NULL, NULL);
1057 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(spec_dialog->treeview));
1058 gtk_tree_selection_set_mode(sel, GTK_SELECTION_BROWSE);
1059
1060 g_signal_connect(G_OBJECT(dialog), "response",
1061 G_CALLBACK(email_response_cb), spec_dialog);
1062 g_signal_connect(G_OBJECT(sel), "changed",
1063 G_CALLBACK(selection_changed_cb), spec_dialog);
1064 g_signal_connect(G_OBJECT(spec_dialog->treeview), "row-activated", G_CALLBACK(email_row_activated_cb), NULL);
1065
1066 column = gtk_tree_view_column_new();
1067 gtk_tree_view_column_set_resizable(column, TRUE);
1068 rend = gtk_cell_renderer_pixbuf_new();
1069 gtk_tree_view_column_pack_start(column, rend, FALSE);
1070
1071 gtk_tree_view_column_set_attributes(column, rend, "pixbuf", PIDGIN_MAIL_ICON, NULL);
1072 rend = gtk_cell_renderer_text_new();
1073 gtk_tree_view_column_pack_start(column, rend, TRUE);
1074 gtk_tree_view_column_set_attributes(column, rend, "markup", PIDGIN_MAIL_TEXT, NULL);
1075 gtk_tree_view_append_column(GTK_TREE_VIEW(spec_dialog->treeview), column);
1076
1077 label = gtk_label_new(NULL);
1078 gtk_label_set_markup(GTK_LABEL(label), _("<span weight=\"bold\" size=\"larger\">You have mail!</span>"));
1079 }
1080
1081 gtk_dialog_add_button(GTK_DIALOG(dialog),
1082 _("Close"), GTK_RESPONSE_CLOSE);
1083
1084 gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
1085 gtk_label_set_xalign(GTK_LABEL(label), 0);
1086 gtk_label_set_yalign(GTK_LABEL(label), 0);
1087 gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
1088 gtk_box_pack_start(GTK_BOX(vbox),
1089 pidgin_make_scrollable(spec_dialog->treeview, GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS, GTK_SHADOW_IN, -1, -1),
1090 TRUE, TRUE, 2);
1091
1092 return spec_dialog;
1093 }
1094
1095 static void
1096 signed_off_cb(PurpleConnection *gc, gpointer unused)
1097 {
1098 /* Clear any pending emails for this account */
1099 pidgin_notify_emails(gc, 0, FALSE, NULL, NULL, NULL, NULL);
1100
1101 if (mail_dialog != NULL && mail_dialog->total_count == 0)
1102 reset_mail_dialog(NULL);
1103 }
1104
1105 static void* 614 static void*
1106 pidgin_notify_get_handle(void) 615 pidgin_notify_get_handle(void)
1107 { 616 {
1108 static int handle; 617 static int handle;
1109 return &handle; 618 return &handle;
1110 } 619 }
1111 620
1112 void pidgin_notify_init(void) 621 void pidgin_notify_init(void)
1113 { 622 {
1114 void *handle = pidgin_notify_get_handle();
1115
1116 purple_signal_connect(purple_connections_get_handle(), "signed-off",
1117 handle, PURPLE_CALLBACK(signed_off_cb), NULL);
1118 } 623 }
1119 624
1120 void pidgin_notify_uninit(void) 625 void pidgin_notify_uninit(void)
1121 { 626 {
1122 purple_signals_disconnect_by_handle(pidgin_notify_get_handle()); 627 purple_signals_disconnect_by_handle(pidgin_notify_get_handle());
1123 } 628 }
1124 629
1125 static PurpleNotifyUiOps ops = 630 static PurpleNotifyUiOps ops =
1126 { 631 {
1127 pidgin_notify_message, 632 pidgin_notify_message,
1128 pidgin_notify_email, 633 NULL,
1129 pidgin_notify_emails, 634 NULL,
1130 pidgin_notify_formatted, 635 pidgin_notify_formatted,
1131 pidgin_notify_searchresults, 636 pidgin_notify_searchresults,
1132 pidgin_notify_searchresults_new_rows, 637 pidgin_notify_searchresults_new_rows,
1133 pidgin_notify_userinfo, 638 pidgin_notify_userinfo,
1134 pidgin_notify_uri, 639 pidgin_notify_uri,

mercurial