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
;
960 PidginNotifySearchResultsData
*data
;
962 char *primary_esc
, *secondary_esc
;
964 g_return_val_if_fail(gc
!= NULL
, NULL
);
965 g_return_val_if_fail(results
!= NULL
, NULL
);
967 data
= g_malloc(sizeof(PidginNotifySearchResultsData
));
968 data
->user_data
= user_data
;
969 data
->results
= results
;
971 /* Create the window */
972 window
= gtk_dialog_new();
973 gtk_window_set_title(GTK_WINDOW(window
), title
? title
:_("Search Results"));
974 gtk_container_set_border_width(GTK_CONTAINER(window
), PIDGIN_HIG_BORDER
);
975 gtk_window_set_resizable(GTK_WINDOW(window
), TRUE
);
977 g_signal_connect_swapped(G_OBJECT(window
), "delete_event",
978 G_CALLBACK(searchresults_close_cb
), data
);
980 /* Setup the main vbox */
981 vbox
= GTK_DIALOG(window
)->vbox
;
983 /* Setup the descriptive label */
984 primary_esc
= (primary
!= NULL
) ? g_markup_escape_text(primary
, -1) : NULL
;
985 secondary_esc
= (secondary
!= NULL
) ? g_markup_escape_text(secondary
, -1) : NULL
;
986 label_text
= g_strdup_printf(
987 "<span weight=\"bold\" size=\"larger\">%s</span>%s%s",
988 (primary
? primary_esc
: ""),
989 (primary
&& secondary
? "\n" : ""),
990 (secondary
? secondary_esc
: ""));
992 g_free(secondary_esc
);
993 label
= gtk_label_new(NULL
);
994 gtk_label_set_markup(GTK_LABEL(label
), label_text
);
995 gtk_label_set_line_wrap(GTK_LABEL(label
), TRUE
);
996 gtk_misc_set_alignment(GTK_MISC(label
), 0, 0);
997 gtk_box_pack_start(GTK_BOX(vbox
), label
, FALSE
, FALSE
, 0);
998 gtk_widget_show(label
);
1001 /* +1 is for the automagically created Status column. */
1002 col_num
= g_list_length(results
->columns
) + 1;
1004 /* Setup the list model */
1005 col_types
= g_new0(GType
, col_num
);
1007 /* There always is this first column. */
1008 col_types
[0] = GDK_TYPE_PIXBUF
;
1009 for (i
= 1; i
< col_num
; i
++) {
1010 col_types
[i
] = G_TYPE_STRING
;
1012 model
= gtk_list_store_newv(col_num
, col_types
);
1015 /* Setup the scrolled window containing the treeview */
1016 sw
= gtk_scrolled_window_new(NULL
, NULL
);
1017 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw
),
1018 GTK_POLICY_AUTOMATIC
, GTK_POLICY_ALWAYS
);
1019 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw
),
1021 gtk_box_pack_start(GTK_BOX(vbox
), sw
, TRUE
, TRUE
, 0);
1022 gtk_widget_show(sw
);
1024 /* Setup the treeview */
1025 treeview
= gtk_tree_view_new_with_model(GTK_TREE_MODEL(model
));
1026 g_object_unref(G_OBJECT(model
));
1027 gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(treeview
), TRUE
);
1028 gtk_widget_set_size_request(treeview
, 500, 400);
1029 gtk_tree_selection_set_mode(gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview
)),
1030 GTK_SELECTION_SINGLE
);
1031 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(treeview
), TRUE
);
1032 gtk_container_add(GTK_CONTAINER(sw
), treeview
);
1033 gtk_widget_show(treeview
);
1035 renderer
= gtk_cell_renderer_pixbuf_new();
1036 gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(treeview
),
1037 -1, "", renderer
, "pixbuf", 0, NULL
);
1040 for (columniter
= results
->columns
; columniter
!= NULL
; columniter
= columniter
->next
) {
1041 PurpleNotifySearchColumn
*column
= columniter
->data
;
1042 renderer
= gtk_cell_renderer_text_new();
1044 gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(treeview
), -1,
1045 column
->title
, renderer
, "text", i
, NULL
);
1049 for (l
= results
->buttons
; l
; l
= l
->next
) {
1050 PurpleNotifySearchButton
*b
= l
->data
;
1051 GtkWidget
*button
= NULL
;
1053 case PURPLE_NOTIFY_BUTTON_LABELED
:
1055 button
= gtk_button_new_with_label(b
->label
);
1057 purple_debug_warning("gtknotify", "Missing button label\n");
1060 case PURPLE_NOTIFY_BUTTON_CONTINUE
:
1061 button
= gtk_dialog_add_button(GTK_DIALOG(window
), GTK_STOCK_GO_FORWARD
, GTK_RESPONSE_NONE
);
1063 case PURPLE_NOTIFY_BUTTON_ADD
:
1064 button
= gtk_dialog_add_button(GTK_DIALOG(window
), GTK_STOCK_ADD
, GTK_RESPONSE_NONE
);
1066 case PURPLE_NOTIFY_BUTTON_INFO
:
1067 button
= gtk_dialog_add_button(GTK_DIALOG(window
), PIDGIN_STOCK_TOOLBAR_USER_INFO
, GTK_RESPONSE_NONE
);
1069 case PURPLE_NOTIFY_BUTTON_IM
:
1070 button
= gtk_dialog_add_button(GTK_DIALOG(window
), PIDGIN_STOCK_TOOLBAR_MESSAGE_NEW
, GTK_RESPONSE_NONE
);
1072 case PURPLE_NOTIFY_BUTTON_JOIN
:
1073 button
= gtk_dialog_add_button(GTK_DIALOG(window
), PIDGIN_STOCK_CHAT
, GTK_RESPONSE_NONE
);
1075 case PURPLE_NOTIFY_BUTTON_INVITE
:
1076 button
= gtk_dialog_add_button(GTK_DIALOG(window
), PIDGIN_STOCK_INVITE
, GTK_RESPONSE_NONE
);
1079 purple_debug_warning("gtknotify", "Incorrect button type: %d\n", b
->type
);
1081 if (button
!= NULL
) {
1082 PidginNotifySearchResultsButtonData
*bd
;
1084 bd
= g_new0(PidginNotifySearchResultsButtonData
, 1);
1088 g_signal_connect(G_OBJECT(button
), "clicked",
1089 G_CALLBACK(searchresults_callback_wrapper_cb
), bd
);
1090 g_signal_connect_swapped(G_OBJECT(button
), "destroy", G_CALLBACK(g_free
), bd
);
1094 /* Add the Close button */
1095 close_button
= gtk_dialog_add_button(GTK_DIALOG(window
), GTK_STOCK_CLOSE
, GTK_RESPONSE_CLOSE
);
1097 g_signal_connect_swapped(G_OBJECT(close_button
), "clicked",
1098 G_CALLBACK(searchresults_close_cb
), data
);
1100 data
->account
= gc
->account
;
1101 data
->model
= model
;
1102 data
->treeview
= treeview
;
1103 data
->window
= window
;
1106 pidgin_notify_searchresults_new_rows(gc
, results
, data
);
1108 /* Show the window */
1109 pidgin_auto_parent_window(window
);
1111 gtk_widget_show(window
);
1115 /** Xerox'ed from Finch! How the tables have turned!! ;) **/
1116 /** User information. **/
1117 static GHashTable
*userinfo
;
1120 userinfo_hash(PurpleAccount
*account
, const char *who
)
1123 snprintf(key
, sizeof(key
), "%s - %s", purple_account_get_username(account
), purple_normalize(account
, who
));
1124 return g_utf8_strup(key
, -1);
1128 remove_userinfo(GtkWidget
*widget
, gpointer key
)
1130 PidginUserInfo
*pinfo
= g_hash_table_lookup(userinfo
, key
);
1132 while (pinfo
->count
--)
1133 purple_notify_close(PURPLE_NOTIFY_USERINFO
, widget
);
1135 g_hash_table_remove(userinfo
, key
);
1139 pidgin_notify_userinfo(PurpleConnection
*gc
, const char *who
,
1140 PurpleNotifyUserInfo
*user_info
)
1144 char *key
= userinfo_hash(purple_connection_get_account(gc
), who
);
1145 PidginUserInfo
*pinfo
= NULL
;
1148 userinfo
= g_hash_table_new_full(g_str_hash
, g_str_equal
, g_free
, g_free
);
1151 info
= purple_notify_user_info_get_text_with_newline(user_info
, "<br />");
1152 pinfo
= g_hash_table_lookup(userinfo
, key
);
1153 if (pinfo
!= NULL
) {
1154 GtkIMHtml
*imhtml
= g_object_get_data(G_OBJECT(pinfo
->window
), "info-widget");
1155 char *linked_text
= purple_markup_linkify(info
);
1156 gtk_imhtml_clear(imhtml
);
1157 gtk_imhtml_append_text(imhtml
, linked_text
, notify_imhtml_options());
1158 g_free(linked_text
);
1160 ui_handle
= pinfo
->window
;
1163 char *primary
= g_strdup_printf(_("Info for %s"), who
);
1164 ui_handle
= pidgin_notify_formatted(_("Buddy Information"), primary
, NULL
, info
);
1165 g_signal_handlers_disconnect_by_func(G_OBJECT(ui_handle
), G_CALLBACK(formatted_close_cb
), NULL
);
1166 g_signal_connect(G_OBJECT(ui_handle
), "destroy", G_CALLBACK(remove_userinfo
), key
);
1168 pinfo
= g_new0(PidginUserInfo
, 1);
1169 pinfo
->window
= ui_handle
;
1171 g_hash_table_insert(userinfo
, key
, pinfo
);
1178 pidgin_close_notify(PurpleNotifyType type
, void *ui_handle
)
1180 if (type
== PURPLE_NOTIFY_EMAIL
|| type
== PURPLE_NOTIFY_EMAILS
)
1182 PidginNotifyMailData
*data
= (PidginNotifyMailData
*)ui_handle
;
1189 else if (type
== PURPLE_NOTIFY_SEARCHRESULTS
)
1191 PidginNotifySearchResultsData
*data
= (PidginNotifySearchResultsData
*)ui_handle
;
1193 gtk_widget_destroy(data
->window
);
1194 purple_notify_searchresults_free(data
->results
);
1198 else if (ui_handle
!= NULL
)
1199 gtk_widget_destroy(GTK_WIDGET(ui_handle
));
1204 uri_command(const char *command
, gboolean sync
)
1207 GError
*error
= NULL
;
1210 purple_debug_misc("gtknotify", "Executing %s\n", command
);
1212 if (!purple_program_is_valid(command
))
1214 tmp
= g_strdup_printf(_("The browser command \"%s\" is invalid."),
1215 command
? command
: "(none)");
1216 purple_notify_error(NULL
, NULL
, _("Unable to open URL"), tmp
);
1224 if (!g_spawn_command_line_sync(command
, NULL
, NULL
, &status
, &error
))
1226 tmp
= g_strdup_printf(_("Error launching \"%s\": %s"),
1227 command
, error
->message
);
1228 purple_notify_error(NULL
, NULL
, _("Unable to open URL"), tmp
);
1230 g_error_free(error
);
1237 if (!g_spawn_command_line_async(command
, &error
))
1239 tmp
= g_strdup_printf(_("Error launching \"%s\": %s"),
1240 command
, error
->message
);
1241 purple_notify_error(NULL
, NULL
, _("Unable to open URL"), tmp
);
1243 g_error_free(error
);
1252 pidgin_notify_uri(const char *uri
)
1255 char *escaped
= g_shell_quote(uri
);
1256 char *command
= NULL
;
1257 char *remote_command
= NULL
;
1258 const char *web_browser
;
1261 web_browser
= purple_prefs_get_string(PIDGIN_PREFS_ROOT
"/browsers/browser");
1262 place
= purple_prefs_get_int(PIDGIN_PREFS_ROOT
"/browsers/place");
1264 /* if they are running gnome, use the gnome web browser */
1265 if (purple_running_gnome() == TRUE
)
1267 char *tmp
= g_find_program_in_path("xdg-open");
1269 command
= g_strdup_printf("gnome-open %s", escaped
);
1271 command
= g_strdup_printf("xdg-open %s", escaped
);
1274 else if (purple_running_osx() == TRUE
)
1276 command
= g_strdup_printf("open %s", escaped
);
1278 else if (!strcmp(web_browser
, "epiphany") ||
1279 !strcmp(web_browser
, "galeon"))
1281 if (place
== PIDGIN_BROWSER_NEW_WINDOW
)
1282 command
= g_strdup_printf("%s -w %s", web_browser
, escaped
);
1283 else if (place
== PIDGIN_BROWSER_NEW_TAB
)
1284 command
= g_strdup_printf("%s -n %s", web_browser
, escaped
);
1286 command
= g_strdup_printf("%s %s", web_browser
, escaped
);
1288 else if (!strcmp(web_browser
, "xdg-open"))
1290 command
= g_strdup_printf("xdg-open %s", escaped
);
1292 else if (!strcmp(web_browser
, "gnome-open"))
1294 command
= g_strdup_printf("gnome-open %s", escaped
);
1296 else if (!strcmp(web_browser
, "kfmclient"))
1298 command
= g_strdup_printf("kfmclient openURL %s", escaped
);
1300 * Does Konqueror have options to open in new tab
1301 * and/or current window?
1304 else if (!strcmp(web_browser
, "mozilla") ||
1305 !strcmp(web_browser
, "mozilla-firebird") ||
1306 !strcmp(web_browser
, "firefox") ||
1307 !strcmp(web_browser
, "seamonkey"))
1311 command
= g_strdup_printf("%s %s", web_browser
, escaped
);
1314 * Firefox 0.9 and higher require a "-a firefox" option when
1315 * using -remote commands. This breaks older versions of
1316 * mozilla. So we include this other handly little string
1317 * when calling firefox. If the API for remote calls changes
1318 * any more in firefox then firefox should probably be split
1319 * apart from mozilla-firebird and mozilla... but this is good
1322 if (!strcmp(web_browser
, "firefox"))
1323 args
= "-a firefox";
1325 if (place
== PIDGIN_BROWSER_NEW_WINDOW
)
1326 remote_command
= g_strdup_printf("%s %s -remote "
1327 "openURL(%s,new-window)",
1328 web_browser
, args
, escaped
);
1329 else if (place
== PIDGIN_BROWSER_NEW_TAB
)
1330 remote_command
= g_strdup_printf("%s %s -remote "
1331 "openURL(%s,new-tab)",
1332 web_browser
, args
, escaped
);
1333 else if (place
== PIDGIN_BROWSER_CURRENT
)
1334 remote_command
= g_strdup_printf("%s %s -remote "
1336 web_browser
, args
, escaped
);
1338 else if (!strcmp(web_browser
, "netscape"))
1340 command
= g_strdup_printf("netscape %s", escaped
);
1342 if (place
== PIDGIN_BROWSER_NEW_WINDOW
)
1344 remote_command
= g_strdup_printf("netscape -remote "
1345 "openURL(%s,new-window)",
1348 else if (place
== PIDGIN_BROWSER_CURRENT
)
1350 remote_command
= g_strdup_printf("netscape -remote "
1351 "openURL(%s)", escaped
);
1354 else if (!strcmp(web_browser
, "opera"))
1356 if (place
== PIDGIN_BROWSER_NEW_WINDOW
)
1357 command
= g_strdup_printf("opera -newwindow %s", escaped
);
1358 else if (place
== PIDGIN_BROWSER_NEW_TAB
)
1359 command
= g_strdup_printf("opera -newpage %s", escaped
);
1360 else if (place
== PIDGIN_BROWSER_CURRENT
)
1362 remote_command
= g_strdup_printf("opera -remote "
1363 "openURL(%s)", escaped
);
1364 command
= g_strdup_printf("opera %s", escaped
);
1367 command
= g_strdup_printf("opera %s", escaped
);
1370 else if (!strcmp(web_browser
, "google-chrome"))
1372 /* Google Chrome doesn't have command-line arguments that control the
1373 * opening of links from external calls. This is controlled solely from
1374 * a preference within Google Chrome. */
1375 command
= g_strdup_printf("google-chrome %s", escaped
);
1377 else if (!strcmp(web_browser
, "chrome"))
1379 /* Chromium doesn't have command-line arguments that control the
1380 * opening of links from external calls. This is controlled solely from
1381 * a preference within Chromium. */
1382 command
= g_strdup_printf("chrome %s", escaped
);
1384 else if (!strcmp(web_browser
, "chromium-browser"))
1386 /* Chromium doesn't have command-line arguments that control the
1387 * opening of links from external calls. This is controlled solely from
1388 * a preference within Chromium. */
1389 command
= g_strdup_printf("chromium-browser %s", escaped
);
1391 else if (!strcmp(web_browser
, "custom"))
1393 const char *web_command
;
1395 web_command
= purple_prefs_get_string(PIDGIN_PREFS_ROOT
"/browsers/manual_command");
1397 if (web_command
== NULL
|| *web_command
== '\0')
1399 purple_notify_error(NULL
, NULL
, _("Unable to open URL"),
1400 _("The 'Manual' browser command has been "
1401 "chosen, but no command has been set."));
1405 if (strstr(web_command
, "%s"))
1406 command
= purple_strreplace(web_command
, "%s", escaped
);
1410 * There is no "%s" in the browser command. Assume the user
1411 * wanted the URL tacked on to the end of the command.
1413 command
= g_strdup_printf("%s %s", web_command
, escaped
);
1419 if (remote_command
!= NULL
)
1421 /* try the remote command first */
1422 if (uri_command(remote_command
, TRUE
) != 0)
1423 uri_command(command
, FALSE
);
1425 g_free(remote_command
);
1429 uri_command(command
, FALSE
);
1434 winpidgin_notify_uri(uri
);
1435 #endif /* !_WIN32 */
1441 pidgin_notify_pounce_add(PurpleAccount
*account
, PurplePounce
*pounce
,
1442 const char *alias
, const char *event
, const char *message
, const char *date
)
1446 PidginNotifyPounceData
*pounce_data
;
1447 gboolean first
= (pounce_dialog
== NULL
);
1449 if (pounce_dialog
== NULL
)
1450 pounce_dialog
= pidgin_create_notification_dialog(PIDGIN_NOTIFY_POUNCE
);
1452 icon
= pidgin_create_prpl_icon(account
, PIDGIN_PRPL_ICON_SMALL
);
1454 pounce_data
= g_new(PidginNotifyPounceData
, 1);
1456 pounce_data
->account
= account
;
1457 pounce_data
->pounce
= pounce
;
1458 pounce_data
->pouncee
= g_strdup(purple_pounce_get_pouncee(pounce
));
1460 gtk_tree_store_append(pounce_dialog
->treemodel
, &iter
, NULL
);
1462 gtk_tree_store_set(pounce_dialog
->treemodel
, &iter
,
1463 PIDGIN_POUNCE_ICON
, icon
,
1464 PIDGIN_POUNCE_ALIAS
, alias
,
1465 PIDGIN_POUNCE_EVENT
, event
,
1466 PIDGIN_POUNCE_TEXT
, (message
!= NULL
)? message
: _("No message"),
1467 PIDGIN_POUNCE_DATE
, date
,
1468 PIDGIN_POUNCE_DATA
, pounce_data
,
1472 GtkTreeSelection
*selection
=
1473 gtk_tree_view_get_selection(GTK_TREE_VIEW(pounce_dialog
->treeview
));
1474 gtk_tree_selection_select_iter(selection
, &iter
);
1478 g_object_unref(icon
);
1480 gtk_widget_show_all(pounce_dialog
->dialog
);
1485 static PidginNotifyDialog
*
1486 pidgin_create_notification_dialog(PidginNotifyType type
)
1488 GtkTreeStore
*model
= NULL
;
1489 GtkWidget
*dialog
= NULL
;
1490 GtkWidget
*label
= NULL
;
1492 GtkCellRenderer
*rend
;
1493 GtkTreeViewColumn
*column
;
1494 GtkWidget
*button
= NULL
;
1495 GtkWidget
*vbox
= NULL
;
1496 GtkTreeSelection
*sel
;
1497 PidginNotifyDialog
*spec_dialog
= NULL
;
1499 g_return_val_if_fail(type
< PIDGIN_NOTIFY_TYPES
, NULL
);
1501 if (type
== PIDGIN_NOTIFY_MAIL
) {
1502 g_return_val_if_fail(mail_dialog
== NULL
, mail_dialog
);
1504 model
= gtk_tree_store_new(COLUMNS_PIDGIN_MAIL
,
1505 GDK_TYPE_PIXBUF
, G_TYPE_STRING
, G_TYPE_POINTER
);
1507 } else if (type
== PIDGIN_NOTIFY_POUNCE
) {
1508 g_return_val_if_fail(pounce_dialog
== NULL
, pounce_dialog
);
1510 model
= gtk_tree_store_new(COLUMNS_PIDGIN_POUNCE
,
1511 GDK_TYPE_PIXBUF
, G_TYPE_STRING
, G_TYPE_STRING
, G_TYPE_STRING
,
1512 G_TYPE_STRING
, G_TYPE_POINTER
);
1515 dialog
= gtk_dialog_new();
1517 /* Setup the dialog */
1518 gtk_container_set_border_width(GTK_CONTAINER(dialog
), PIDGIN_HIG_BOX_SPACE
);
1519 gtk_container_set_border_width(GTK_CONTAINER(GTK_DIALOG(dialog
)->vbox
), PIDGIN_HIG_BOX_SPACE
);
1520 gtk_dialog_set_has_separator(GTK_DIALOG(dialog
), FALSE
);
1521 gtk_box_set_spacing(GTK_BOX(GTK_DIALOG(dialog
)->vbox
), PIDGIN_HIG_BORDER
);
1524 vbox
= GTK_DIALOG(dialog
)->vbox
;
1526 /* Golden ratio it up! */
1527 gtk_widget_set_size_request(dialog
, 550, 400);
1529 sw
= gtk_scrolled_window_new(NULL
, NULL
);
1530 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW(sw
), GTK_SHADOW_IN
);
1531 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw
), GTK_POLICY_AUTOMATIC
, GTK_POLICY_ALWAYS
);
1533 spec_dialog
= g_new0(PidginNotifyDialog
, 1);
1534 spec_dialog
->dialog
= dialog
;
1536 spec_dialog
->treemodel
= model
;
1537 spec_dialog
->treeview
= gtk_tree_view_new_with_model(GTK_TREE_MODEL(model
));
1538 g_object_unref(G_OBJECT(model
));
1540 gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(spec_dialog
->treeview
), TRUE
);
1541 gtk_container_add(GTK_CONTAINER(sw
), spec_dialog
->treeview
);
1543 if (type
== PIDGIN_NOTIFY_MAIL
) {
1544 gtk_window_set_title(GTK_WINDOW(dialog
), _("New Mail"));
1545 gtk_window_set_role(GTK_WINDOW(dialog
), "new_mail_detailed");
1546 g_signal_connect(G_OBJECT(dialog
), "focus-in-event",
1547 G_CALLBACK(mail_window_focus_cb
), NULL
);
1549 gtk_dialog_add_button(GTK_DIALOG(dialog
),
1550 _("Open All Messages"), GTK_RESPONSE_ACCEPT
);
1552 button
= gtk_dialog_add_button(GTK_DIALOG(dialog
),
1553 PIDGIN_STOCK_OPEN_MAIL
, GTK_RESPONSE_YES
);
1554 spec_dialog
->open_button
= button
;
1556 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(spec_dialog
->treeview
), FALSE
);
1558 gtk_tree_view_set_search_column(GTK_TREE_VIEW(spec_dialog
->treeview
), PIDGIN_MAIL_TEXT
);
1559 gtk_tree_view_set_search_equal_func(GTK_TREE_VIEW(spec_dialog
->treeview
),
1560 pidgin_tree_view_search_equal_func
, NULL
, NULL
);
1562 g_signal_connect(G_OBJECT(dialog
), "response",
1563 G_CALLBACK(email_response_cb
), spec_dialog
);
1564 g_signal_connect(G_OBJECT(gtk_tree_view_get_selection(GTK_TREE_VIEW(spec_dialog
->treeview
))),
1565 "changed", G_CALLBACK(selection_changed_cb
), spec_dialog
);
1566 g_signal_connect(G_OBJECT(spec_dialog
->treeview
), "row-activated", G_CALLBACK(email_row_activated_cb
), NULL
);
1568 column
= gtk_tree_view_column_new();
1569 gtk_tree_view_column_set_resizable(column
, TRUE
);
1570 rend
= gtk_cell_renderer_pixbuf_new();
1571 gtk_tree_view_column_pack_start(column
, rend
, FALSE
);
1573 gtk_tree_view_column_set_attributes(column
, rend
, "pixbuf", PIDGIN_MAIL_ICON
, NULL
);
1574 rend
= gtk_cell_renderer_text_new();
1575 gtk_tree_view_column_pack_start(column
, rend
, TRUE
);
1576 gtk_tree_view_column_set_attributes(column
, rend
, "markup", PIDGIN_MAIL_TEXT
, NULL
);
1577 gtk_tree_view_append_column(GTK_TREE_VIEW(spec_dialog
->treeview
), column
);
1579 label
= gtk_label_new(NULL
);
1580 gtk_label_set_markup(GTK_LABEL(label
), _("<span weight=\"bold\" size=\"larger\">You have mail!</span>"));
1582 } else if (type
== PIDGIN_NOTIFY_POUNCE
) {
1583 gtk_window_set_title(GTK_WINDOW(dialog
), _("New Pounces"));
1585 button
= gtk_dialog_add_button(GTK_DIALOG(dialog
),
1586 _("IM"), GTK_RESPONSE_YES
);
1587 gtk_widget_set_sensitive(button
, FALSE
);
1588 spec_dialog
->open_button
= button
;
1590 button
= gtk_dialog_add_button(GTK_DIALOG(dialog
),
1591 PIDGIN_STOCK_MODIFY
, GTK_RESPONSE_APPLY
);
1592 gtk_widget_set_sensitive(button
, FALSE
);
1593 spec_dialog
->edit_button
= button
;
1595 button
= gtk_dialog_add_button(GTK_DIALOG(dialog
),
1596 _("Dismiss"), GTK_RESPONSE_NO
);
1597 gtk_widget_set_sensitive(button
, FALSE
);
1598 spec_dialog
->dismiss_button
= button
;
1600 g_signal_connect(G_OBJECT(dialog
), "response",
1601 G_CALLBACK(pounce_response_cb
), spec_dialog
);
1603 column
= gtk_tree_view_column_new();
1604 gtk_tree_view_column_set_title(column
, _("Buddy"));
1605 gtk_tree_view_column_set_resizable(column
, TRUE
);
1606 rend
= gtk_cell_renderer_pixbuf_new();
1607 gtk_tree_view_column_pack_start(column
, rend
, FALSE
);
1609 gtk_tree_view_column_set_attributes(column
, rend
, "pixbuf", PIDGIN_POUNCE_ICON
, NULL
);
1610 rend
= gtk_cell_renderer_text_new();
1611 gtk_tree_view_column_pack_start(column
, rend
, FALSE
);
1612 gtk_tree_view_column_add_attribute(column
, rend
, "text", PIDGIN_POUNCE_ALIAS
);
1613 gtk_tree_view_append_column(GTK_TREE_VIEW(spec_dialog
->treeview
), column
);
1615 column
= gtk_tree_view_column_new();
1616 gtk_tree_view_column_set_title(column
, _("Event"));
1617 gtk_tree_view_column_set_resizable(column
, TRUE
);
1618 rend
= gtk_cell_renderer_text_new();
1619 gtk_tree_view_column_pack_start(column
, rend
, FALSE
);
1620 gtk_tree_view_column_add_attribute(column
, rend
, "text", PIDGIN_POUNCE_EVENT
);
1621 gtk_tree_view_append_column(GTK_TREE_VIEW(spec_dialog
->treeview
), column
);
1623 column
= gtk_tree_view_column_new();
1624 gtk_tree_view_column_set_title(column
, _("Message"));
1625 gtk_tree_view_column_set_resizable(column
, TRUE
);
1626 rend
= gtk_cell_renderer_text_new();
1627 gtk_tree_view_column_pack_start(column
, rend
, FALSE
);
1628 gtk_tree_view_column_add_attribute(column
, rend
, "text", PIDGIN_POUNCE_TEXT
);
1629 gtk_tree_view_append_column(GTK_TREE_VIEW(spec_dialog
->treeview
), column
);
1631 column
= gtk_tree_view_column_new();
1632 gtk_tree_view_column_set_title(column
, _("Date"));
1633 gtk_tree_view_column_set_resizable(column
, TRUE
);
1634 rend
= gtk_cell_renderer_text_new();
1635 gtk_tree_view_column_pack_start(column
, rend
, FALSE
);
1636 gtk_tree_view_column_add_attribute(column
, rend
, "text", PIDGIN_POUNCE_DATE
);
1637 gtk_tree_view_append_column(GTK_TREE_VIEW(spec_dialog
->treeview
), column
);
1639 label
= gtk_label_new(NULL
);
1640 gtk_label_set_markup(GTK_LABEL(label
), _("<span weight=\"bold\" size=\"larger\">You have pounced!</span>"));
1642 sel
= gtk_tree_view_get_selection(GTK_TREE_VIEW(spec_dialog
->treeview
));
1643 gtk_tree_selection_set_mode(sel
, GTK_SELECTION_MULTIPLE
);
1644 g_signal_connect(G_OBJECT(sel
), "changed",
1645 G_CALLBACK(pounce_row_selected_cb
), NULL
);
1646 g_signal_connect(G_OBJECT(spec_dialog
->treeview
), "row-activated",
1647 G_CALLBACK(pounce_response_open_ims
), NULL
);
1650 button
= gtk_dialog_add_button(GTK_DIALOG(dialog
),
1651 GTK_STOCK_CLOSE
, GTK_RESPONSE_CLOSE
);
1653 gtk_label_set_line_wrap(GTK_LABEL(label
), TRUE
);
1654 gtk_misc_set_alignment(GTK_MISC(label
), 0, 0);
1655 gtk_box_pack_start(GTK_BOX(vbox
), label
, FALSE
, FALSE
, 0);
1656 gtk_box_pack_start(GTK_BOX(vbox
), sw
, TRUE
, TRUE
, 2);
1662 signed_off_cb(PurpleConnection
*gc
, gpointer unused
)
1664 /* Clear any pending emails for this account */
1665 pidgin_notify_emails(gc
, 0, FALSE
, NULL
, NULL
, NULL
, NULL
);
1667 if (mail_dialog
!= NULL
&& mail_dialog
->total_count
== 0)
1668 reset_mail_dialog(NULL
);
1672 pidgin_notify_get_handle(void)
1678 void pidgin_notify_init(void)
1680 void *handle
= pidgin_notify_get_handle();
1682 purple_signal_connect(purple_connections_get_handle(), "signed-off",
1683 handle
, PURPLE_CALLBACK(signed_off_cb
), NULL
);
1686 void pidgin_notify_uninit(void)
1688 purple_signals_disconnect_by_handle(pidgin_notify_get_handle());
1691 static PurpleNotifyUiOps ops
=
1693 pidgin_notify_message
,
1694 pidgin_notify_email
,
1695 pidgin_notify_emails
,
1696 pidgin_notify_formatted
,
1697 pidgin_notify_searchresults
,
1698 pidgin_notify_searchresults_new_rows
,
1699 pidgin_notify_userinfo
,
1701 pidgin_close_notify
,
1709 pidgin_notify_get_ui_ops(void)