2 * @file gtknotify.c GTK+ Notification API
8 * Pidgin 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
29 #include <gdk/gdkkeysyms.h>
32 #include "connection.h"
35 #include "pidginstock.h"
39 #include "gtkimhtml.h"
40 #include "gtknotify.h"
41 #include "gtkpounce.h"
52 PurpleAccount
*account
;
56 gboolean purple_has_handle
;
57 } PidginNotifyMailData
;
61 PurpleAccount
*account
;
64 } PidginNotifyPounceData
;
69 PurpleAccount
*account
;
74 PurpleNotifySearchResults
*results
;
76 } PidginNotifySearchResultsData
;
80 PurpleNotifySearchButton
*button
;
81 PidginNotifySearchResultsData
*data
;
83 } PidginNotifySearchResultsButtonData
;
101 COLUMNS_PIDGIN_POUNCE
105 typedef struct _PidginNotifyDialog
108 * This must be first so PidginNotifyDialog can masquerade as the
113 GtkTreeStore
*treemodel
;
115 GtkWidget
*open_button
;
116 GtkWidget
*dismiss_button
;
117 GtkWidget
*edit_button
;
120 } PidginNotifyDialog
;
125 PIDGIN_NOTIFY_POUNCE
,
129 static PidginNotifyDialog
*mail_dialog
= NULL
;
130 static PidginNotifyDialog
*pounce_dialog
= NULL
;
132 static PidginNotifyDialog
*pidgin_create_notification_dialog(PidginNotifyType type
);
133 static void *pidgin_notify_emails(PurpleConnection
*gc
, size_t count
, gboolean detailed
,
134 const char **subjects
,
135 const char **froms
, const char **tos
,
138 static void pidgin_close_notify(PurpleNotifyType type
, void *ui_handle
);
141 message_response_cb(GtkDialog
*dialog
, gint id
, GtkWidget
*widget
)
143 purple_notify_close(PURPLE_NOTIFY_MESSAGE
, widget
);
147 pounce_response_close(PidginNotifyDialog
*dialog
)
150 PidginNotifyPounceData
*pounce_data
;
152 while (gtk_tree_model_get_iter_first(
153 GTK_TREE_MODEL(pounce_dialog
->treemodel
), &iter
)) {
154 gtk_tree_model_get(GTK_TREE_MODEL(pounce_dialog
->treemodel
), &iter
,
155 PIDGIN_POUNCE_DATA
, &pounce_data
,
157 gtk_tree_store_remove(dialog
->treemodel
, &iter
);
159 g_free(pounce_data
->pouncee
);
163 gtk_widget_destroy(pounce_dialog
->dialog
);
164 g_free(pounce_dialog
);
165 pounce_dialog
= NULL
;
169 delete_foreach(GtkTreeModel
*model
, GtkTreePath
*path
,
170 GtkTreeIter
*iter
, gpointer data
)
172 PidginNotifyPounceData
*pounce_data
;
174 gtk_tree_model_get(model
, iter
,
175 PIDGIN_POUNCE_DATA
, &pounce_data
,
178 if (pounce_data
!= NULL
) {
179 g_free(pounce_data
->pouncee
);
185 open_im_foreach(GtkTreeModel
*model
, GtkTreePath
*path
,
186 GtkTreeIter
*iter
, gpointer data
)
188 PidginNotifyPounceData
*pounce_data
;
190 gtk_tree_model_get(model
, iter
,
191 PIDGIN_POUNCE_DATA
, &pounce_data
,
194 if (pounce_data
!= NULL
) {
195 PurpleConversation
*conv
;
197 conv
= purple_conversation_new(PURPLE_CONV_TYPE_IM
,
198 pounce_data
->account
, pounce_data
->pouncee
);
199 purple_conversation_present(conv
);
204 append_to_list(GtkTreeModel
*model
, GtkTreePath
*path
,
205 GtkTreeIter
*iter
, gpointer data
)
208 *list
= g_list_prepend(*list
, gtk_tree_path_copy(path
));
212 pounce_response_dismiss()
214 GtkTreeModel
*model
= GTK_TREE_MODEL(pounce_dialog
->treemodel
);
215 GtkTreeSelection
*selection
;
217 GtkTreeIter new_selection
;
219 gboolean found_selection
= FALSE
;
221 selection
= gtk_tree_view_get_selection(GTK_TREE_VIEW(pounce_dialog
->treeview
));
222 gtk_tree_selection_selected_foreach(selection
, delete_foreach
, pounce_dialog
);
223 gtk_tree_selection_selected_foreach(selection
, append_to_list
, &list
);
225 g_return_if_fail(list
!= NULL
);
227 if (list
->next
== NULL
) {
228 gtk_tree_model_get_iter(model
, &new_selection
, list
->data
);
229 if (gtk_tree_model_iter_next(model
, &new_selection
))
230 found_selection
= TRUE
;
232 /* This is the last thing in the list */
235 /* Because gtk_tree_model_iter_prev doesn't exist... */
236 gtk_tree_model_get_iter(model
, &new_selection
, list
->data
);
237 path
= gtk_tree_model_get_path(model
, &new_selection
);
238 if (gtk_tree_path_prev(path
)) {
239 gtk_tree_model_get_iter(model
, &new_selection
, path
);
240 found_selection
= TRUE
;
243 gtk_tree_path_free(path
);
248 if (gtk_tree_model_get_iter(model
, &iter
, list
->data
)) {
249 gtk_tree_store_remove(GTK_TREE_STORE(pounce_dialog
->treemodel
), &iter
);
251 gtk_tree_path_free(list
->data
);
252 list
= g_list_delete_link(list
, list
);
255 if (gtk_tree_model_get_iter_first(model
, &iter
)) {
257 gtk_tree_selection_select_iter(selection
, &new_selection
);
259 gtk_tree_selection_select_iter(selection
, &iter
);
261 pounce_response_close(pounce_dialog
);
265 pounce_response_open_ims()
267 GtkTreeSelection
*selection
;
269 selection
= gtk_tree_view_get_selection(GTK_TREE_VIEW(pounce_dialog
->treeview
));
270 gtk_tree_selection_selected_foreach(selection
, open_im_foreach
, pounce_dialog
);
272 pounce_response_dismiss();
276 pounce_response_edit_cb(GtkTreeModel
*model
, GtkTreePath
*path
,
277 GtkTreeIter
*iter
, gpointer data
)
279 PidginNotifyPounceData
*pounce_data
;
280 PidginNotifyDialog
*dialog
= (PidginNotifyDialog
*)data
;
281 PurplePounce
*pounce
;
284 list
= purple_pounces_get_all();
286 gtk_tree_model_get(GTK_TREE_MODEL(dialog
->treemodel
), iter
,
287 PIDGIN_POUNCE_DATA
, &pounce_data
,
290 for (; list
!= NULL
; list
= list
->next
) {
292 if (pounce
== pounce_data
->pounce
) {
293 pidgin_pounce_editor_show(pounce_data
->account
, NULL
, pounce_data
->pounce
);
298 purple_debug_warning("gtknotify", "Pounce was destroyed.\n");
302 pounce_response_cb(GtkDialog
*dlg
, gint id
, PidginNotifyDialog
*dialog
)
304 GtkTreeSelection
*selection
= NULL
;
307 case GTK_RESPONSE_CLOSE
:
308 case GTK_RESPONSE_DELETE_EVENT
:
309 pounce_response_close(dialog
);
311 case GTK_RESPONSE_YES
:
312 pounce_response_open_ims();
314 case GTK_RESPONSE_NO
:
315 pounce_response_dismiss();
317 case GTK_RESPONSE_APPLY
:
318 selection
= gtk_tree_view_get_selection(GTK_TREE_VIEW(dialog
->treeview
));
319 gtk_tree_selection_selected_foreach(selection
, pounce_response_edit_cb
,
326 pounce_row_selected_cb(GtkTreeView
*tv
, GtkTreePath
*path
,
327 GtkTreeViewColumn
*col
, gpointer data
)
329 GtkTreeSelection
*selection
;
332 selection
= gtk_tree_view_get_selection(GTK_TREE_VIEW(pounce_dialog
->treeview
));
334 count
= gtk_tree_selection_count_selected_rows(selection
);
337 gtk_widget_set_sensitive(pounce_dialog
->open_button
, FALSE
);
338 gtk_widget_set_sensitive(pounce_dialog
->edit_button
, FALSE
);
339 gtk_widget_set_sensitive(pounce_dialog
->dismiss_button
, FALSE
);
340 } else if (count
== 1) {
343 PidginNotifyPounceData
*pounce_data
;
346 list
= gtk_tree_selection_get_selected_rows(selection
, NULL
);
347 gtk_tree_model_get_iter(GTK_TREE_MODEL(pounce_dialog
->treemodel
),
349 gtk_tree_model_get(GTK_TREE_MODEL(pounce_dialog
->treemodel
), &iter
,
350 PIDGIN_POUNCE_DATA
, &pounce_data
,
352 g_list_foreach(list
, (GFunc
)gtk_tree_path_free
, NULL
);
355 pounces
= purple_pounces_get_all();
356 for (; pounces
!= NULL
; pounces
= pounces
->next
) {
357 PurplePounce
*pounce
= pounces
->data
;
358 if (pounce
== pounce_data
->pounce
) {
359 gtk_widget_set_sensitive(pounce_dialog
->edit_button
, TRUE
);
364 gtk_widget_set_sensitive(pounce_dialog
->open_button
, TRUE
);
365 gtk_widget_set_sensitive(pounce_dialog
->dismiss_button
, TRUE
);
367 gtk_widget_set_sensitive(pounce_dialog
->open_button
, TRUE
);
368 gtk_widget_set_sensitive(pounce_dialog
->edit_button
, FALSE
);
369 gtk_widget_set_sensitive(pounce_dialog
->dismiss_button
, TRUE
);
376 reset_mail_dialog(GtkDialog
*unused
)
378 if (mail_dialog
->in_use
)
380 gtk_widget_destroy(mail_dialog
->dialog
);
386 email_response_cb(GtkDialog
*unused
, gint id
, PidginNotifyDialog
*unused2
)
388 PidginNotifyMailData
*data
= NULL
;
389 GtkTreeModel
*model
= GTK_TREE_MODEL(mail_dialog
->treemodel
);
392 if (id
== GTK_RESPONSE_YES
)
394 /* A single row activated. Remove that row. */
395 GtkTreeSelection
*selection
;
397 selection
= gtk_tree_view_get_selection(GTK_TREE_VIEW(mail_dialog
->treeview
));
399 if (gtk_tree_selection_get_selected(selection
, NULL
, &iter
))
401 gtk_tree_model_get(model
, &iter
, PIDGIN_MAIL_DATA
, &data
, -1);
402 purple_notify_uri(NULL
, data
->url
);
404 gtk_tree_store_remove(mail_dialog
->treemodel
, &iter
);
405 if (data
->purple_has_handle
)
406 purple_notify_close(PURPLE_NOTIFY_EMAILS
, data
);
408 pidgin_close_notify(PURPLE_NOTIFY_EMAILS
, data
);
410 if (gtk_tree_model_get_iter_first(model
, &iter
))
418 /* Remove all the rows */
419 while (gtk_tree_model_get_iter_first(model
, &iter
))
421 gtk_tree_model_get(model
, &iter
, PIDGIN_MAIL_DATA
, &data
, -1);
423 if (id
== GTK_RESPONSE_ACCEPT
)
424 purple_notify_uri(NULL
, data
->url
);
426 gtk_tree_store_remove(mail_dialog
->treemodel
, &iter
);
427 if (data
->purple_has_handle
)
428 purple_notify_close(PURPLE_NOTIFY_EMAILS
, data
);
430 pidgin_close_notify(PURPLE_NOTIFY_EMAILS
, data
);
434 reset_mail_dialog(NULL
);
438 email_row_activated_cb(GtkTreeView
*tv
, GtkTreePath
*path
,
439 GtkTreeViewColumn
*col
, gpointer data
)
441 email_response_cb(NULL
, GTK_RESPONSE_YES
, NULL
);
445 formatted_close_cb(GtkWidget
*win
, GdkEvent
*event
, void *user_data
)
447 purple_notify_close(PURPLE_NOTIFY_FORMATTED
, win
);
452 searchresults_close_cb(PidginNotifySearchResultsData
*data
, GdkEvent
*event
, gpointer user_data
)
454 purple_notify_close(PURPLE_NOTIFY_SEARCHRESULTS
, data
);
459 searchresults_callback_wrapper_cb(GtkWidget
*widget
, PidginNotifySearchResultsButtonData
*bd
)
461 PidginNotifySearchResultsData
*data
= bd
->data
;
463 GtkTreeSelection
*selection
;
466 PurpleNotifySearchButton
*button
;
471 g_return_if_fail(data
!= NULL
);
473 selection
= gtk_tree_view_get_selection(GTK_TREE_VIEW(data
->treeview
));
475 if (gtk_tree_selection_get_selected(selection
, &model
, &iter
))
477 for (i
= 1; i
< gtk_tree_model_get_n_columns(GTK_TREE_MODEL(model
)); i
++) {
478 gtk_tree_model_get(GTK_TREE_MODEL(model
), &iter
, i
, &str
, -1);
479 row
= g_list_append(row
, str
);
484 button
->callback(purple_account_get_connection(data
->account
), row
, data
->user_data
);
485 g_list_foreach(row
, (GFunc
)g_free
, NULL
);
490 pidgin_notify_message(PurpleNotifyMsgType type
, const char *title
,
491 const char *primary
, const char *secondary
)
496 GtkWidget
*img
= NULL
;
497 char label_text
[2048];
498 const char *icon_name
= NULL
;
499 char *primary_esc
, *secondary_esc
;
503 case PURPLE_NOTIFY_MSG_ERROR
:
504 icon_name
= PIDGIN_STOCK_DIALOG_ERROR
;
507 case PURPLE_NOTIFY_MSG_WARNING
:
508 icon_name
= PIDGIN_STOCK_DIALOG_WARNING
;
511 case PURPLE_NOTIFY_MSG_INFO
:
512 icon_name
= PIDGIN_STOCK_DIALOG_INFO
;
520 if (icon_name
!= NULL
)
522 img
= gtk_image_new_from_stock(icon_name
, gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_HUGE
));
523 gtk_misc_set_alignment(GTK_MISC(img
), 0, 0);
526 dialog
= gtk_dialog_new_with_buttons(title
? title
: PIDGIN_ALERT_TITLE
,
527 NULL
, 0, GTK_STOCK_CLOSE
,
528 GTK_RESPONSE_CLOSE
, NULL
);
530 gtk_window_set_role(GTK_WINDOW(dialog
), "notify_dialog");
532 g_signal_connect(G_OBJECT(dialog
), "response",
533 G_CALLBACK(message_response_cb
), dialog
);
535 gtk_container_set_border_width(GTK_CONTAINER(dialog
), PIDGIN_HIG_BORDER
);
536 gtk_window_set_resizable(GTK_WINDOW(dialog
), FALSE
);
537 gtk_dialog_set_has_separator(GTK_DIALOG(dialog
), FALSE
);
538 gtk_box_set_spacing(GTK_BOX(GTK_DIALOG(dialog
)->vbox
), PIDGIN_HIG_BORDER
);
539 gtk_container_set_border_width(GTK_CONTAINER(GTK_DIALOG(dialog
)->vbox
), PIDGIN_HIG_BOX_SPACE
);
541 hbox
= gtk_hbox_new(FALSE
, PIDGIN_HIG_BORDER
);
542 gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog
)->vbox
), hbox
);
545 gtk_box_pack_start(GTK_BOX(hbox
), img
, FALSE
, FALSE
, 0);
547 primary_esc
= g_markup_escape_text(primary
, -1);
548 secondary_esc
= (secondary
!= NULL
) ? g_markup_escape_text(secondary
, -1) : NULL
;
549 g_snprintf(label_text
, sizeof(label_text
),
550 "<span weight=\"bold\" size=\"larger\">%s</span>%s%s",
551 primary_esc
, (secondary
? "\n\n" : ""),
552 (secondary
? secondary_esc
: ""));
554 g_free(secondary_esc
);
556 label
= gtk_label_new(NULL
);
558 gtk_label_set_markup(GTK_LABEL(label
), label_text
);
559 gtk_label_set_line_wrap(GTK_LABEL(label
), TRUE
);
560 gtk_label_set_selectable(GTK_LABEL(label
), TRUE
);
561 gtk_misc_set_alignment(GTK_MISC(label
), 0, 0);
562 gtk_box_pack_start(GTK_BOX(hbox
), label
, FALSE
, FALSE
, 0);
564 pidgin_auto_parent_window(dialog
);
566 gtk_widget_show_all(dialog
);
572 selection_changed_cb(GtkTreeSelection
*sel
, PidginNotifyDialog
*dialog
)
576 PidginNotifyMailData
*data
;
577 gboolean active
= TRUE
;
579 if (gtk_tree_selection_get_selected(sel
, &model
, &iter
) == FALSE
)
583 gtk_tree_model_get(model
, &iter
, PIDGIN_MAIL_DATA
, &data
, -1);
584 if (data
->url
== NULL
)
588 gtk_widget_set_sensitive(dialog
->open_button
, active
);
592 pidgin_notify_email(PurpleConnection
*gc
, const char *subject
, const char *from
,
593 const char *to
, const char *url
)
595 return pidgin_notify_emails(gc
, 1, (subject
!= NULL
),
596 (subject
== NULL
? NULL
: &subject
),
597 (from
== NULL
? NULL
: &from
),
598 (to
== NULL
? NULL
: &to
),
599 (url
== NULL
? NULL
: &url
));
603 mail_window_focus_cb(GtkWidget
*widget
, GdkEventFocus
*focus
, gpointer null
)
605 pidgin_set_urgent(GTK_WINDOW(widget
), FALSE
);
609 /* count == 0 means this is a detailed mail notification.
610 * count > 0 mean non-detailed.
613 pidgin_notify_add_mail(GtkTreeStore
*treemodel
, PurpleAccount
*account
, char *notification
, const char *url
, int count
, gboolean clear
, gboolean
*new_data
)
615 PidginNotifyMailData
*data
= NULL
;
618 gboolean new_n
= TRUE
;
620 if (count
> 0 || clear
) {
621 /* Allow only one non-detailed email notification for each account */
622 if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(treemodel
), &iter
)) {
626 gtk_tree_model_get(GTK_TREE_MODEL(treemodel
), &iter
,
627 PIDGIN_MAIL_DATA
, &data
, -1);
628 if (data
&& data
->account
== account
) {
630 advanced
= gtk_tree_store_remove(treemodel
, &iter
);
631 mail_dialog
->total_count
-= data
->count
;
633 if (data
->purple_has_handle
)
634 purple_notify_close(PURPLE_NOTIFY_EMAILS
, data
);
636 pidgin_close_notify(PURPLE_NOTIFY_EMAILS
, data
);
637 /* We're completely done if we've processed all entries */
640 } else if (data
->count
> 0) {
644 mail_dialog
->total_count
-= data
->count
;
648 } while (advanced
|| gtk_tree_model_iter_next(GTK_TREE_MODEL(treemodel
), &iter
));
655 icon
= pidgin_create_prpl_icon(account
, PIDGIN_PRPL_ICON_MEDIUM
);
658 data
= g_new0(PidginNotifyMailData
, 1);
659 data
->purple_has_handle
= TRUE
;
660 gtk_tree_store_append(treemodel
, &iter
, NULL
);
664 data
->url
= g_strdup(url
);
666 gtk_tree_store_set(treemodel
, &iter
,
667 PIDGIN_MAIL_ICON
, icon
,
668 PIDGIN_MAIL_TEXT
, notification
,
669 PIDGIN_MAIL_DATA
, data
,
671 data
->account
= account
;
672 /* count == 0 indicates we're adding a single detailed e-mail */
673 data
->count
= count
> 0 ? count
: 1;
676 g_object_unref(icon
);
684 pidgin_notify_emails(PurpleConnection
*gc
, size_t count
, gboolean detailed
,
685 const char **subjects
, const char **froms
,
686 const char **tos
, const char **urls
)
689 PurpleAccount
*account
;
690 PidginNotifyMailData
*data
= NULL
, *data2
;
691 gboolean new_data
= FALSE
;
693 /* Don't bother updating if there aren't new emails and we don't have any displayed currently */
694 if (count
== 0 && mail_dialog
== NULL
)
697 account
= purple_connection_get_account(gc
);
698 if (mail_dialog
== NULL
)
699 mail_dialog
= pidgin_create_notification_dialog(PIDGIN_NOTIFY_MAIL
);
701 mail_dialog
->total_count
+= count
;
703 for ( ; count
; --count
) {
704 char *to_text
= NULL
;
705 char *from_text
= NULL
;
706 char *subject_text
= NULL
;
708 gboolean first
= TRUE
;
711 tmp
= g_markup_escape_text(*tos
, -1);
712 to_text
= g_strdup_printf("<b>%s</b>: %s\n", _("Account"), tmp
);
718 tmp
= g_markup_escape_text(*froms
, -1);
719 from_text
= g_strdup_printf("%s<b>%s</b>: %s\n", first
? "<br>" : "", _("Sender"), tmp
);
724 if (subjects
!= NULL
) {
725 tmp
= g_markup_escape_text(*subjects
, -1);
726 subject_text
= g_strdup_printf("%s<b>%s</b>: %s", first
? "<br>" : "", _("Subject"), tmp
);
731 #define SAFE(x) ((x) ? (x) : "")
732 notification
= g_strdup_printf("%s%s%s", SAFE(to_text
), SAFE(from_text
), SAFE(subject_text
));
736 g_free(subject_text
);
738 /* If we don't keep track of this, will leak "data" for each of the notifications except the last */
739 data2
= pidgin_notify_add_mail(mail_dialog
->treemodel
, account
, notification
, urls
? *urls
: NULL
, 0, FALSE
, &new_data
);
740 if (data2
&& new_data
) {
742 data
->purple_has_handle
= FALSE
;
745 g_free(notification
);
752 notification
= g_strdup_printf(ngettext("%s has %d new message.",
753 "%s has %d new messages.",
756 data2
= pidgin_notify_add_mail(mail_dialog
->treemodel
, account
, notification
, urls
? *urls
: NULL
, count
, FALSE
, &new_data
);
757 if (data2
&& new_data
) {
759 data
->purple_has_handle
= FALSE
;
762 g_free(notification
);
764 /* Clear out all mails for the account */
765 pidgin_notify_add_mail(mail_dialog
->treemodel
, account
, NULL
, NULL
, 0, TRUE
, NULL
);
767 if (mail_dialog
->total_count
== 0) {
769 * There is no API to clear the headline specifically
770 * This will trigger reset_mail_dialog()
772 pidgin_blist_set_headline(NULL
, NULL
, NULL
, NULL
, NULL
);
778 if (!GTK_WIDGET_VISIBLE(mail_dialog
->dialog
)) {
779 GdkPixbuf
*pixbuf
= gtk_widget_render_icon(mail_dialog
->dialog
, PIDGIN_STOCK_DIALOG_MAIL
,
780 gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_EXTRA_SMALL
), NULL
);
781 char *label_text
= g_strdup_printf(ngettext("<b>%d new email.</b>",
782 "<b>%d new emails.</b>",
783 mail_dialog
->total_count
), mail_dialog
->total_count
);
784 mail_dialog
->in_use
= TRUE
; /* So that _set_headline doesn't accidentally
785 remove the notifications when replacing an
787 pidgin_blist_set_headline(label_text
,
788 pixbuf
, G_CALLBACK(gtk_widget_show_all
), mail_dialog
->dialog
,
789 (GDestroyNotify
)reset_mail_dialog
);
790 mail_dialog
->in_use
= FALSE
;
793 g_object_unref(pixbuf
);
794 } else if (!GTK_WIDGET_HAS_FOCUS(mail_dialog
->dialog
))
795 pidgin_set_urgent(GTK_WINDOW(mail_dialog
->dialog
), TRUE
);
801 formatted_input_cb(GtkWidget
*win
, GdkEventKey
*event
, gpointer data
)
803 if (event
->keyval
== GDK_Escape
)
805 purple_notify_close(PURPLE_NOTIFY_FORMATTED
, win
);
813 static GtkIMHtmlOptions
814 notify_imhtml_options(void)
816 GtkIMHtmlOptions options
= 0;
818 if (!purple_prefs_get_bool(PIDGIN_PREFS_ROOT
"/conversations/show_incoming_formatting"))
819 options
|= GTK_IMHTML_NO_COLOURS
| GTK_IMHTML_NO_FONTS
| GTK_IMHTML_NO_SIZES
;
821 options
|= GTK_IMHTML_NO_COMMENTS
;
822 options
|= GTK_IMHTML_NO_TITLE
;
823 options
|= GTK_IMHTML_NO_NEWLINE
;
824 options
|= GTK_IMHTML_NO_SCROLL
;
829 pidgin_notify_formatted(const char *title
, const char *primary
,
830 const char *secondary
, const char *text
)
838 char label_text
[2048];
839 char *linked_text
, *primary_esc
, *secondary_esc
;
841 window
= gtk_dialog_new();
842 gtk_window_set_title(GTK_WINDOW(window
), title
);
843 gtk_container_set_border_width(GTK_CONTAINER(window
), PIDGIN_HIG_BORDER
);
844 gtk_window_set_resizable(GTK_WINDOW(window
), TRUE
);
846 g_signal_connect(G_OBJECT(window
), "delete_event",
847 G_CALLBACK(formatted_close_cb
), NULL
);
849 /* Setup the main vbox */
850 vbox
= GTK_DIALOG(window
)->vbox
;
852 /* Setup the descriptive label */
853 primary_esc
= g_markup_escape_text(primary
, -1);
854 secondary_esc
= (secondary
!= NULL
) ? g_markup_escape_text(secondary
, -1) : NULL
;
855 g_snprintf(label_text
, sizeof(label_text
),
856 "<span weight=\"bold\" size=\"larger\">%s</span>%s%s",
858 (secondary
? "\n" : ""),
859 (secondary
? secondary_esc
: ""));
861 g_free(secondary_esc
);
863 label
= gtk_label_new(NULL
);
865 gtk_label_set_markup(GTK_LABEL(label
), label_text
);
866 gtk_label_set_line_wrap(GTK_LABEL(label
), TRUE
);
867 gtk_label_set_selectable(GTK_LABEL(label
), TRUE
);
868 gtk_misc_set_alignment(GTK_MISC(label
), 0, 0);
869 gtk_box_pack_start(GTK_BOX(vbox
), label
, FALSE
, FALSE
, 0);
870 gtk_widget_show(label
);
873 frame
= pidgin_create_imhtml(FALSE
, &imhtml
, NULL
, NULL
);
874 gtk_widget_set_name(imhtml
, "pidgin_notify_imhtml");
875 gtk_imhtml_set_format_functions(GTK_IMHTML(imhtml
),
876 gtk_imhtml_get_format_functions(GTK_IMHTML(imhtml
)) | GTK_IMHTML_IMAGE
);
877 gtk_widget_set_size_request(imhtml
, 300, 250);
878 gtk_box_pack_start(GTK_BOX(vbox
), frame
, TRUE
, TRUE
, 0);
879 gtk_widget_show(frame
);
881 /* Add the Close button. */
882 button
= gtk_dialog_add_button(GTK_DIALOG(window
), GTK_STOCK_CLOSE
, GTK_RESPONSE_CLOSE
);
883 gtk_widget_grab_focus(button
);
885 g_signal_connect_swapped(G_OBJECT(button
), "clicked",
886 G_CALLBACK(formatted_close_cb
), window
);
887 g_signal_connect(G_OBJECT(window
), "key_press_event",
888 G_CALLBACK(formatted_input_cb
), NULL
);
890 /* Make sure URLs are clickable */
891 linked_text
= purple_markup_linkify(text
);
892 gtk_imhtml_append_text(GTK_IMHTML(imhtml
), linked_text
, notify_imhtml_options());
895 g_object_set_data(G_OBJECT(window
), "info-widget", imhtml
);
897 /* Show the window */
898 pidgin_auto_parent_window(window
);
900 gtk_widget_show(window
);
906 pidgin_notify_searchresults_new_rows(PurpleConnection
*gc
, PurpleNotifySearchResults
*results
,
909 PidginNotifySearchResultsData
*data
= data_
;
910 GtkListStore
*model
= data
->model
;
916 gtk_list_store_clear(data
->model
);
918 pixbuf
= pidgin_create_prpl_icon(purple_connection_get_account(gc
), 0.5);
920 for (row
= results
->rows
; row
!= NULL
; row
= row
->next
) {
922 gtk_list_store_append(model
, &iter
);
923 gtk_list_store_set(model
, &iter
, 0, pixbuf
, -1);
926 for (column
= row
->data
; column
!= NULL
; column
= column
->next
) {
930 g_value_init(&v
, G_TYPE_STRING
);
931 g_value_set_string(&v
, column
->data
);
932 gtk_list_store_set_value(model
, &iter
, n
, &v
);
938 g_object_unref(pixbuf
);
942 pidgin_notify_searchresults(PurpleConnection
*gc
, const char *title
,
943 const char *primary
, const char *secondary
,
944 PurpleNotifySearchResults
*results
, gpointer user_data
)
948 GtkWidget
*close_button
;
951 GtkCellRenderer
*renderer
;
959 PidginNotifySearchResultsData
*data
;
961 char *primary_esc
, *secondary_esc
;
963 g_return_val_if_fail(gc
!= NULL
, NULL
);
964 g_return_val_if_fail(results
!= NULL
, NULL
);
966 data
= g_malloc(sizeof(PidginNotifySearchResultsData
));
967 data
->user_data
= user_data
;
968 data
->results
= results
;
970 /* Create the window */
971 window
= gtk_dialog_new();
972 gtk_window_set_title(GTK_WINDOW(window
), title
? title
:_("Search Results"));
973 gtk_container_set_border_width(GTK_CONTAINER(window
), PIDGIN_HIG_BORDER
);
974 gtk_window_set_resizable(GTK_WINDOW(window
), TRUE
);
976 g_signal_connect_swapped(G_OBJECT(window
), "delete_event",
977 G_CALLBACK(searchresults_close_cb
), data
);
979 /* Setup the main vbox */
980 vbox
= GTK_DIALOG(window
)->vbox
;
982 /* Setup the descriptive label */
983 primary_esc
= (primary
!= NULL
) ? g_markup_escape_text(primary
, -1) : NULL
;
984 secondary_esc
= (secondary
!= NULL
) ? g_markup_escape_text(secondary
, -1) : NULL
;
985 label_text
= g_strdup_printf(
986 "<span weight=\"bold\" size=\"larger\">%s</span>%s%s",
987 (primary
? primary_esc
: ""),
988 (primary
&& secondary
? "\n" : ""),
989 (secondary
? secondary_esc
: ""));
991 g_free(secondary_esc
);
992 label
= gtk_label_new(NULL
);
993 gtk_label_set_markup(GTK_LABEL(label
), label_text
);
994 gtk_label_set_line_wrap(GTK_LABEL(label
), TRUE
);
995 gtk_misc_set_alignment(GTK_MISC(label
), 0, 0);
996 gtk_box_pack_start(GTK_BOX(vbox
), label
, FALSE
, FALSE
, 0);
997 gtk_widget_show(label
);
1000 /* +1 is for the automagically created Status column. */
1001 col_num
= g_list_length(results
->columns
) + 1;
1003 /* Setup the list model */
1004 col_types
= g_new0(GType
, col_num
);
1006 /* There always is this first column. */
1007 col_types
[0] = GDK_TYPE_PIXBUF
;
1008 for (i
= 1; i
< col_num
; i
++) {
1009 col_types
[i
] = G_TYPE_STRING
;
1011 model
= gtk_list_store_newv(col_num
, col_types
);
1014 /* Setup the treeview */
1015 treeview
= gtk_tree_view_new_with_model(GTK_TREE_MODEL(model
));
1016 g_object_unref(G_OBJECT(model
));
1017 gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(treeview
), TRUE
);
1018 gtk_widget_set_size_request(treeview
, 500, 400);
1019 gtk_tree_selection_set_mode(gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview
)),
1020 GTK_SELECTION_SINGLE
);
1021 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(treeview
), TRUE
);
1022 gtk_box_pack_start(GTK_BOX(vbox
),
1023 pidgin_make_scrollable(treeview
, GTK_POLICY_AUTOMATIC
, GTK_POLICY_ALWAYS
, GTK_SHADOW_IN
, -1, -1),
1025 gtk_widget_show(treeview
);
1027 renderer
= gtk_cell_renderer_pixbuf_new();
1028 gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(treeview
),
1029 -1, "", renderer
, "pixbuf", 0, NULL
);
1032 for (columniter
= results
->columns
; columniter
!= NULL
; columniter
= columniter
->next
) {
1033 PurpleNotifySearchColumn
*column
= columniter
->data
;
1034 renderer
= gtk_cell_renderer_text_new();
1036 gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(treeview
), -1,
1037 column
->title
, renderer
, "text", i
, NULL
);
1041 for (l
= results
->buttons
; l
; l
= l
->next
) {
1042 PurpleNotifySearchButton
*b
= l
->data
;
1043 GtkWidget
*button
= NULL
;
1045 case PURPLE_NOTIFY_BUTTON_LABELED
:
1047 button
= gtk_button_new_with_label(b
->label
);
1049 purple_debug_warning("gtknotify", "Missing button label\n");
1052 case PURPLE_NOTIFY_BUTTON_CONTINUE
:
1053 button
= gtk_dialog_add_button(GTK_DIALOG(window
), GTK_STOCK_GO_FORWARD
, GTK_RESPONSE_NONE
);
1055 case PURPLE_NOTIFY_BUTTON_ADD
:
1056 button
= gtk_dialog_add_button(GTK_DIALOG(window
), GTK_STOCK_ADD
, GTK_RESPONSE_NONE
);
1058 case PURPLE_NOTIFY_BUTTON_INFO
:
1059 button
= gtk_dialog_add_button(GTK_DIALOG(window
), PIDGIN_STOCK_TOOLBAR_USER_INFO
, GTK_RESPONSE_NONE
);
1061 case PURPLE_NOTIFY_BUTTON_IM
:
1062 button
= gtk_dialog_add_button(GTK_DIALOG(window
), PIDGIN_STOCK_TOOLBAR_MESSAGE_NEW
, GTK_RESPONSE_NONE
);
1064 case PURPLE_NOTIFY_BUTTON_JOIN
:
1065 button
= gtk_dialog_add_button(GTK_DIALOG(window
), PIDGIN_STOCK_CHAT
, GTK_RESPONSE_NONE
);
1067 case PURPLE_NOTIFY_BUTTON_INVITE
:
1068 button
= gtk_dialog_add_button(GTK_DIALOG(window
), PIDGIN_STOCK_INVITE
, GTK_RESPONSE_NONE
);
1071 purple_debug_warning("gtknotify", "Incorrect button type: %d\n", b
->type
);
1073 if (button
!= NULL
) {
1074 PidginNotifySearchResultsButtonData
*bd
;
1076 bd
= g_new0(PidginNotifySearchResultsButtonData
, 1);
1080 g_signal_connect(G_OBJECT(button
), "clicked",
1081 G_CALLBACK(searchresults_callback_wrapper_cb
), bd
);
1082 g_signal_connect_swapped(G_OBJECT(button
), "destroy", G_CALLBACK(g_free
), bd
);
1086 /* Add the Close button */
1087 close_button
= gtk_dialog_add_button(GTK_DIALOG(window
), GTK_STOCK_CLOSE
, GTK_RESPONSE_CLOSE
);
1089 g_signal_connect_swapped(G_OBJECT(close_button
), "clicked",
1090 G_CALLBACK(searchresults_close_cb
), data
);
1092 data
->account
= gc
->account
;
1093 data
->model
= model
;
1094 data
->treeview
= treeview
;
1095 data
->window
= window
;
1098 pidgin_notify_searchresults_new_rows(gc
, results
, data
);
1100 /* Show the window */
1101 pidgin_auto_parent_window(window
);
1103 gtk_widget_show(window
);
1107 /** Xerox'ed from Finch! How the tables have turned!! ;) **/
1108 /** User information. **/
1109 static GHashTable
*userinfo
;
1112 userinfo_hash(PurpleAccount
*account
, const char *who
)
1115 snprintf(key
, sizeof(key
), "%s - %s", purple_account_get_username(account
), purple_normalize(account
, who
));
1116 return g_utf8_strup(key
, -1);
1120 remove_userinfo(GtkWidget
*widget
, gpointer key
)
1122 PidginUserInfo
*pinfo
= g_hash_table_lookup(userinfo
, key
);
1124 while (pinfo
->count
--)
1125 purple_notify_close(PURPLE_NOTIFY_USERINFO
, widget
);
1127 g_hash_table_remove(userinfo
, key
);
1131 pidgin_notify_userinfo(PurpleConnection
*gc
, const char *who
,
1132 PurpleNotifyUserInfo
*user_info
)
1136 char *key
= userinfo_hash(purple_connection_get_account(gc
), who
);
1137 PidginUserInfo
*pinfo
= NULL
;
1140 userinfo
= g_hash_table_new_full(g_str_hash
, g_str_equal
, g_free
, g_free
);
1143 info
= purple_notify_user_info_get_text_with_newline(user_info
, "<br />");
1144 pinfo
= g_hash_table_lookup(userinfo
, key
);
1145 if (pinfo
!= NULL
) {
1146 GtkIMHtml
*imhtml
= g_object_get_data(G_OBJECT(pinfo
->window
), "info-widget");
1147 char *linked_text
= purple_markup_linkify(info
);
1148 gtk_imhtml_clear(imhtml
);
1149 gtk_imhtml_append_text(imhtml
, linked_text
, notify_imhtml_options());
1150 g_free(linked_text
);
1152 ui_handle
= pinfo
->window
;
1155 char *primary
= g_strdup_printf(_("Info for %s"), who
);
1156 ui_handle
= pidgin_notify_formatted(_("Buddy Information"), primary
, NULL
, info
);
1157 g_signal_handlers_disconnect_by_func(G_OBJECT(ui_handle
), G_CALLBACK(formatted_close_cb
), NULL
);
1158 g_signal_connect(G_OBJECT(ui_handle
), "destroy", G_CALLBACK(remove_userinfo
), key
);
1160 pinfo
= g_new0(PidginUserInfo
, 1);
1161 pinfo
->window
= ui_handle
;
1163 g_hash_table_insert(userinfo
, key
, pinfo
);
1170 pidgin_close_notify(PurpleNotifyType type
, void *ui_handle
)
1172 if (type
== PURPLE_NOTIFY_EMAIL
|| type
== PURPLE_NOTIFY_EMAILS
)
1174 PidginNotifyMailData
*data
= (PidginNotifyMailData
*)ui_handle
;
1181 else if (type
== PURPLE_NOTIFY_SEARCHRESULTS
)
1183 PidginNotifySearchResultsData
*data
= (PidginNotifySearchResultsData
*)ui_handle
;
1185 gtk_widget_destroy(data
->window
);
1186 purple_notify_searchresults_free(data
->results
);
1190 else if (ui_handle
!= NULL
)
1191 gtk_widget_destroy(GTK_WIDGET(ui_handle
));
1196 uri_command(const char *command
, gboolean sync
)
1199 GError
*error
= NULL
;
1202 purple_debug_misc("gtknotify", "Executing %s\n", command
);
1204 if (!purple_program_is_valid(command
))
1206 tmp
= g_strdup_printf(_("The browser command \"%s\" is invalid."),
1207 command
? command
: "(none)");
1208 purple_notify_error(NULL
, NULL
, _("Unable to open URL"), tmp
);
1216 if (!g_spawn_command_line_sync(command
, NULL
, NULL
, &status
, &error
))
1218 tmp
= g_strdup_printf(_("Error launching \"%s\": %s"),
1219 command
, error
->message
);
1220 purple_notify_error(NULL
, NULL
, _("Unable to open URL"), tmp
);
1222 g_error_free(error
);
1229 if (!g_spawn_command_line_async(command
, &error
))
1231 tmp
= g_strdup_printf(_("Error launching \"%s\": %s"),
1232 command
, error
->message
);
1233 purple_notify_error(NULL
, NULL
, _("Unable to open URL"), tmp
);
1235 g_error_free(error
);
1244 pidgin_notify_uri(const char *uri
)
1247 char *escaped
= g_shell_quote(uri
);
1248 char *command
= NULL
;
1249 char *remote_command
= NULL
;
1250 const char *web_browser
;
1253 web_browser
= purple_prefs_get_string(PIDGIN_PREFS_ROOT
"/browsers/browser");
1254 place
= purple_prefs_get_int(PIDGIN_PREFS_ROOT
"/browsers/place");
1256 /* if they are running gnome, use the gnome web browser */
1257 if (purple_running_gnome() == TRUE
)
1259 char *tmp
= g_find_program_in_path("xdg-open");
1261 command
= g_strdup_printf("gnome-open %s", escaped
);
1263 command
= g_strdup_printf("xdg-open %s", escaped
);
1266 else if (purple_running_osx() == TRUE
)
1268 command
= g_strdup_printf("open %s", escaped
);
1270 else if (!strcmp(web_browser
, "epiphany") ||
1271 !strcmp(web_browser
, "galeon"))
1273 if (place
== PIDGIN_BROWSER_NEW_WINDOW
)
1274 command
= g_strdup_printf("%s -w %s", web_browser
, escaped
);
1275 else if (place
== PIDGIN_BROWSER_NEW_TAB
)
1276 command
= g_strdup_printf("%s -n %s", web_browser
, escaped
);
1278 command
= g_strdup_printf("%s %s", web_browser
, escaped
);
1280 else if (!strcmp(web_browser
, "xdg-open"))
1282 command
= g_strdup_printf("xdg-open %s", escaped
);
1284 else if (!strcmp(web_browser
, "gnome-open"))
1286 command
= g_strdup_printf("gnome-open %s", escaped
);
1288 else if (!strcmp(web_browser
, "kfmclient"))
1290 command
= g_strdup_printf("kfmclient openURL %s", escaped
);
1292 * Does Konqueror have options to open in new tab
1293 * and/or current window?
1296 else if (!strcmp(web_browser
, "mozilla") ||
1297 !strcmp(web_browser
, "mozilla-firebird") ||
1298 !strcmp(web_browser
, "firefox") ||
1299 !strcmp(web_browser
, "seamonkey"))
1303 command
= g_strdup_printf("%s %s", web_browser
, escaped
);
1306 * Firefox 0.9 and higher require a "-a firefox" option when
1307 * using -remote commands. This breaks older versions of
1308 * mozilla. So we include this other handly little string
1309 * when calling firefox. If the API for remote calls changes
1310 * any more in firefox then firefox should probably be split
1311 * apart from mozilla-firebird and mozilla... but this is good
1314 if (!strcmp(web_browser
, "firefox"))
1315 args
= "-a firefox";
1317 if (place
== PIDGIN_BROWSER_NEW_WINDOW
)
1318 remote_command
= g_strdup_printf("%s %s -remote "
1319 "openURL(%s,new-window)",
1320 web_browser
, args
, escaped
);
1321 else if (place
== PIDGIN_BROWSER_NEW_TAB
)
1322 remote_command
= g_strdup_printf("%s %s -remote "
1323 "openURL(%s,new-tab)",
1324 web_browser
, args
, escaped
);
1325 else if (place
== PIDGIN_BROWSER_CURRENT
)
1326 remote_command
= g_strdup_printf("%s %s -remote "
1328 web_browser
, args
, escaped
);
1330 else if (!strcmp(web_browser
, "netscape"))
1332 command
= g_strdup_printf("netscape %s", escaped
);
1334 if (place
== PIDGIN_BROWSER_NEW_WINDOW
)
1336 remote_command
= g_strdup_printf("netscape -remote "
1337 "openURL(%s,new-window)",
1340 else if (place
== PIDGIN_BROWSER_CURRENT
)
1342 remote_command
= g_strdup_printf("netscape -remote "
1343 "openURL(%s)", escaped
);
1346 else if (!strcmp(web_browser
, "opera"))
1348 if (place
== PIDGIN_BROWSER_NEW_WINDOW
)
1349 command
= g_strdup_printf("opera -newwindow %s", escaped
);
1350 else if (place
== PIDGIN_BROWSER_NEW_TAB
)
1351 command
= g_strdup_printf("opera -newpage %s", escaped
);
1352 else if (place
== PIDGIN_BROWSER_CURRENT
)
1354 remote_command
= g_strdup_printf("opera -remote "
1355 "openURL(%s)", escaped
);
1356 command
= g_strdup_printf("opera %s", escaped
);
1359 command
= g_strdup_printf("opera %s", escaped
);
1362 else if (!strcmp(web_browser
, "google-chrome"))
1364 /* Google Chrome doesn't have command-line arguments that control the
1365 * opening of links from external calls. This is controlled solely from
1366 * a preference within Google Chrome. */
1367 command
= g_strdup_printf("google-chrome %s", escaped
);
1369 else if (!strcmp(web_browser
, "chrome"))
1371 /* Chromium doesn't have command-line arguments that control the
1372 * opening of links from external calls. This is controlled solely from
1373 * a preference within Chromium. */
1374 command
= g_strdup_printf("chrome %s", escaped
);
1376 else if (!strcmp(web_browser
, "chromium-browser"))
1378 /* Chromium doesn't have command-line arguments that control the
1379 * opening of links from external calls. This is controlled solely from
1380 * a preference within Chromium. */
1381 command
= g_strdup_printf("chromium-browser %s", escaped
);
1383 else if (!strcmp(web_browser
, "custom"))
1385 const char *web_command
;
1387 web_command
= purple_prefs_get_string(PIDGIN_PREFS_ROOT
"/browsers/manual_command");
1389 if (web_command
== NULL
|| *web_command
== '\0')
1391 purple_notify_error(NULL
, NULL
, _("Unable to open URL"),
1392 _("The 'Manual' browser command has been "
1393 "chosen, but no command has been set."));
1397 if (strstr(web_command
, "%s"))
1398 command
= purple_strreplace(web_command
, "%s", escaped
);
1402 * There is no "%s" in the browser command. Assume the user
1403 * wanted the URL tacked on to the end of the command.
1405 command
= g_strdup_printf("%s %s", web_command
, escaped
);
1411 if (remote_command
!= NULL
)
1413 /* try the remote command first */
1414 if (uri_command(remote_command
, TRUE
) != 0)
1415 uri_command(command
, FALSE
);
1417 g_free(remote_command
);
1421 uri_command(command
, FALSE
);
1426 winpidgin_notify_uri(uri
);
1427 #endif /* !_WIN32 */
1433 pidgin_notify_pounce_add(PurpleAccount
*account
, PurplePounce
*pounce
,
1434 const char *alias
, const char *event
, const char *message
, const char *date
)
1438 PidginNotifyPounceData
*pounce_data
;
1439 gboolean first
= (pounce_dialog
== NULL
);
1441 if (pounce_dialog
== NULL
)
1442 pounce_dialog
= pidgin_create_notification_dialog(PIDGIN_NOTIFY_POUNCE
);
1444 icon
= pidgin_create_prpl_icon(account
, PIDGIN_PRPL_ICON_SMALL
);
1446 pounce_data
= g_new(PidginNotifyPounceData
, 1);
1448 pounce_data
->account
= account
;
1449 pounce_data
->pounce
= pounce
;
1450 pounce_data
->pouncee
= g_strdup(purple_pounce_get_pouncee(pounce
));
1452 gtk_tree_store_append(pounce_dialog
->treemodel
, &iter
, NULL
);
1454 gtk_tree_store_set(pounce_dialog
->treemodel
, &iter
,
1455 PIDGIN_POUNCE_ICON
, icon
,
1456 PIDGIN_POUNCE_ALIAS
, alias
,
1457 PIDGIN_POUNCE_EVENT
, event
,
1458 PIDGIN_POUNCE_TEXT
, (message
!= NULL
)? message
: _("No message"),
1459 PIDGIN_POUNCE_DATE
, date
,
1460 PIDGIN_POUNCE_DATA
, pounce_data
,
1464 GtkTreeSelection
*selection
=
1465 gtk_tree_view_get_selection(GTK_TREE_VIEW(pounce_dialog
->treeview
));
1466 gtk_tree_selection_select_iter(selection
, &iter
);
1470 g_object_unref(icon
);
1472 gtk_widget_show_all(pounce_dialog
->dialog
);
1477 static PidginNotifyDialog
*
1478 pidgin_create_notification_dialog(PidginNotifyType type
)
1480 GtkTreeStore
*model
= NULL
;
1481 GtkWidget
*dialog
= NULL
;
1482 GtkWidget
*label
= NULL
;
1483 GtkCellRenderer
*rend
;
1484 GtkTreeViewColumn
*column
;
1485 GtkWidget
*button
= NULL
;
1486 GtkWidget
*vbox
= NULL
;
1487 GtkTreeSelection
*sel
;
1488 PidginNotifyDialog
*spec_dialog
= NULL
;
1490 g_return_val_if_fail(type
< PIDGIN_NOTIFY_TYPES
, NULL
);
1492 if (type
== PIDGIN_NOTIFY_MAIL
) {
1493 g_return_val_if_fail(mail_dialog
== NULL
, mail_dialog
);
1495 model
= gtk_tree_store_new(COLUMNS_PIDGIN_MAIL
,
1496 GDK_TYPE_PIXBUF
, G_TYPE_STRING
, G_TYPE_POINTER
);
1498 } else if (type
== PIDGIN_NOTIFY_POUNCE
) {
1499 g_return_val_if_fail(pounce_dialog
== NULL
, pounce_dialog
);
1501 model
= gtk_tree_store_new(COLUMNS_PIDGIN_POUNCE
,
1502 GDK_TYPE_PIXBUF
, G_TYPE_STRING
, G_TYPE_STRING
, G_TYPE_STRING
,
1503 G_TYPE_STRING
, G_TYPE_POINTER
);
1506 dialog
= gtk_dialog_new();
1508 /* Setup the dialog */
1509 gtk_container_set_border_width(GTK_CONTAINER(dialog
), PIDGIN_HIG_BOX_SPACE
);
1510 gtk_container_set_border_width(GTK_CONTAINER(GTK_DIALOG(dialog
)->vbox
), PIDGIN_HIG_BOX_SPACE
);
1511 gtk_dialog_set_has_separator(GTK_DIALOG(dialog
), FALSE
);
1512 gtk_box_set_spacing(GTK_BOX(GTK_DIALOG(dialog
)->vbox
), PIDGIN_HIG_BORDER
);
1515 vbox
= GTK_DIALOG(dialog
)->vbox
;
1517 /* Golden ratio it up! */
1518 gtk_widget_set_size_request(dialog
, 550, 400);
1520 spec_dialog
= g_new0(PidginNotifyDialog
, 1);
1521 spec_dialog
->dialog
= dialog
;
1523 spec_dialog
->treemodel
= model
;
1524 spec_dialog
->treeview
= gtk_tree_view_new_with_model(GTK_TREE_MODEL(model
));
1525 g_object_unref(G_OBJECT(model
));
1527 gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(spec_dialog
->treeview
), TRUE
);
1529 if (type
== PIDGIN_NOTIFY_MAIL
) {
1530 gtk_window_set_title(GTK_WINDOW(dialog
), _("New Mail"));
1531 gtk_window_set_role(GTK_WINDOW(dialog
), "new_mail_detailed");
1532 g_signal_connect(G_OBJECT(dialog
), "focus-in-event",
1533 G_CALLBACK(mail_window_focus_cb
), NULL
);
1535 gtk_dialog_add_button(GTK_DIALOG(dialog
),
1536 _("Open All Messages"), GTK_RESPONSE_ACCEPT
);
1538 button
= gtk_dialog_add_button(GTK_DIALOG(dialog
),
1539 PIDGIN_STOCK_OPEN_MAIL
, GTK_RESPONSE_YES
);
1540 spec_dialog
->open_button
= button
;
1542 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(spec_dialog
->treeview
), FALSE
);
1544 gtk_tree_view_set_search_column(GTK_TREE_VIEW(spec_dialog
->treeview
), PIDGIN_MAIL_TEXT
);
1545 gtk_tree_view_set_search_equal_func(GTK_TREE_VIEW(spec_dialog
->treeview
),
1546 pidgin_tree_view_search_equal_func
, NULL
, NULL
);
1548 g_signal_connect(G_OBJECT(dialog
), "response",
1549 G_CALLBACK(email_response_cb
), spec_dialog
);
1550 g_signal_connect(G_OBJECT(gtk_tree_view_get_selection(GTK_TREE_VIEW(spec_dialog
->treeview
))),
1551 "changed", G_CALLBACK(selection_changed_cb
), spec_dialog
);
1552 g_signal_connect(G_OBJECT(spec_dialog
->treeview
), "row-activated", G_CALLBACK(email_row_activated_cb
), NULL
);
1554 column
= gtk_tree_view_column_new();
1555 gtk_tree_view_column_set_resizable(column
, TRUE
);
1556 rend
= gtk_cell_renderer_pixbuf_new();
1557 gtk_tree_view_column_pack_start(column
, rend
, FALSE
);
1559 gtk_tree_view_column_set_attributes(column
, rend
, "pixbuf", PIDGIN_MAIL_ICON
, NULL
);
1560 rend
= gtk_cell_renderer_text_new();
1561 gtk_tree_view_column_pack_start(column
, rend
, TRUE
);
1562 gtk_tree_view_column_set_attributes(column
, rend
, "markup", PIDGIN_MAIL_TEXT
, NULL
);
1563 gtk_tree_view_append_column(GTK_TREE_VIEW(spec_dialog
->treeview
), column
);
1565 label
= gtk_label_new(NULL
);
1566 gtk_label_set_markup(GTK_LABEL(label
), _("<span weight=\"bold\" size=\"larger\">You have mail!</span>"));
1568 } else if (type
== PIDGIN_NOTIFY_POUNCE
) {
1569 gtk_window_set_title(GTK_WINDOW(dialog
), _("New Pounces"));
1571 button
= gtk_dialog_add_button(GTK_DIALOG(dialog
),
1572 _("IM"), GTK_RESPONSE_YES
);
1573 gtk_widget_set_sensitive(button
, FALSE
);
1574 spec_dialog
->open_button
= button
;
1576 button
= gtk_dialog_add_button(GTK_DIALOG(dialog
),
1577 PIDGIN_STOCK_MODIFY
, GTK_RESPONSE_APPLY
);
1578 gtk_widget_set_sensitive(button
, FALSE
);
1579 spec_dialog
->edit_button
= button
;
1581 /* Translators: Make sure you translate "Dismiss" differently than
1582 "close"! This string is used in the "You have pounced" dialog
1583 that appears when one of your Buddy Pounces is triggered. In
1584 this context "Dismiss" means "I acknowledge that I've seen that
1585 this pounce was triggered--remove it from this list." Translating
1586 it as "Remove" is acceptable if you can't think of a more precise
1588 button
= gtk_dialog_add_button(GTK_DIALOG(dialog
), _("Dismiss"),
1590 gtk_widget_set_sensitive(button
, FALSE
);
1591 spec_dialog
->dismiss_button
= button
;
1593 g_signal_connect(G_OBJECT(dialog
), "response",
1594 G_CALLBACK(pounce_response_cb
), spec_dialog
);
1596 column
= gtk_tree_view_column_new();
1597 gtk_tree_view_column_set_title(column
, _("Buddy"));
1598 gtk_tree_view_column_set_resizable(column
, TRUE
);
1599 rend
= gtk_cell_renderer_pixbuf_new();
1600 gtk_tree_view_column_pack_start(column
, rend
, FALSE
);
1602 gtk_tree_view_column_set_attributes(column
, rend
, "pixbuf", PIDGIN_POUNCE_ICON
, NULL
);
1603 rend
= gtk_cell_renderer_text_new();
1604 gtk_tree_view_column_pack_start(column
, rend
, FALSE
);
1605 gtk_tree_view_column_add_attribute(column
, rend
, "text", PIDGIN_POUNCE_ALIAS
);
1606 gtk_tree_view_append_column(GTK_TREE_VIEW(spec_dialog
->treeview
), column
);
1608 column
= gtk_tree_view_column_new();
1609 gtk_tree_view_column_set_title(column
, _("Event"));
1610 gtk_tree_view_column_set_resizable(column
, TRUE
);
1611 rend
= gtk_cell_renderer_text_new();
1612 gtk_tree_view_column_pack_start(column
, rend
, FALSE
);
1613 gtk_tree_view_column_add_attribute(column
, rend
, "text", PIDGIN_POUNCE_EVENT
);
1614 gtk_tree_view_append_column(GTK_TREE_VIEW(spec_dialog
->treeview
), column
);
1616 column
= gtk_tree_view_column_new();
1617 gtk_tree_view_column_set_title(column
, _("Message"));
1618 gtk_tree_view_column_set_resizable(column
, TRUE
);
1619 rend
= gtk_cell_renderer_text_new();
1620 gtk_tree_view_column_pack_start(column
, rend
, FALSE
);
1621 gtk_tree_view_column_add_attribute(column
, rend
, "text", PIDGIN_POUNCE_TEXT
);
1622 gtk_tree_view_append_column(GTK_TREE_VIEW(spec_dialog
->treeview
), column
);
1624 column
= gtk_tree_view_column_new();
1625 gtk_tree_view_column_set_title(column
, _("Date"));
1626 gtk_tree_view_column_set_resizable(column
, TRUE
);
1627 rend
= gtk_cell_renderer_text_new();
1628 gtk_tree_view_column_pack_start(column
, rend
, FALSE
);
1629 gtk_tree_view_column_add_attribute(column
, rend
, "text", PIDGIN_POUNCE_DATE
);
1630 gtk_tree_view_append_column(GTK_TREE_VIEW(spec_dialog
->treeview
), column
);
1632 label
= gtk_label_new(NULL
);
1633 gtk_label_set_markup(GTK_LABEL(label
), _("<span weight=\"bold\" size=\"larger\">You have pounced!</span>"));
1635 sel
= gtk_tree_view_get_selection(GTK_TREE_VIEW(spec_dialog
->treeview
));
1636 gtk_tree_selection_set_mode(sel
, GTK_SELECTION_MULTIPLE
);
1637 g_signal_connect(G_OBJECT(sel
), "changed",
1638 G_CALLBACK(pounce_row_selected_cb
), NULL
);
1639 g_signal_connect(G_OBJECT(spec_dialog
->treeview
), "row-activated",
1640 G_CALLBACK(pounce_response_open_ims
), NULL
);
1643 button
= gtk_dialog_add_button(GTK_DIALOG(dialog
),
1644 GTK_STOCK_CLOSE
, GTK_RESPONSE_CLOSE
);
1646 gtk_label_set_line_wrap(GTK_LABEL(label
), TRUE
);
1647 gtk_misc_set_alignment(GTK_MISC(label
), 0, 0);
1648 gtk_box_pack_start(GTK_BOX(vbox
), label
, FALSE
, FALSE
, 0);
1649 gtk_box_pack_start(GTK_BOX(vbox
),
1650 pidgin_make_scrollable(spec_dialog
->treeview
, GTK_POLICY_AUTOMATIC
, GTK_POLICY_ALWAYS
, GTK_SHADOW_IN
, -1, -1),
1657 signed_off_cb(PurpleConnection
*gc
, gpointer unused
)
1659 /* Clear any pending emails for this account */
1660 pidgin_notify_emails(gc
, 0, FALSE
, NULL
, NULL
, NULL
, NULL
);
1662 if (mail_dialog
!= NULL
&& mail_dialog
->total_count
== 0)
1663 reset_mail_dialog(NULL
);
1667 pidgin_notify_get_handle(void)
1673 void pidgin_notify_init(void)
1675 void *handle
= pidgin_notify_get_handle();
1677 purple_signal_connect(purple_connections_get_handle(), "signed-off",
1678 handle
, PURPLE_CALLBACK(signed_off_cb
), NULL
);
1681 void pidgin_notify_uninit(void)
1683 purple_signals_disconnect_by_handle(pidgin_notify_get_handle());
1686 static PurpleNotifyUiOps ops
=
1688 pidgin_notify_message
,
1689 pidgin_notify_email
,
1690 pidgin_notify_emails
,
1691 pidgin_notify_formatted
,
1692 pidgin_notify_searchresults
,
1693 pidgin_notify_searchresults_new_rows
,
1694 pidgin_notify_userinfo
,
1696 pidgin_close_notify
,
1704 pidgin_notify_get_ui_ops(void)