1 /* Copyright (C) 2006-2007 Peter Zelezny. */
5 #include "../common/xchat-plugin.h"
6 #include "../common/xchat.h"
7 #include "../common/xchatc.h"
8 #include "../common/inbound.h"
9 #include "../common/server.h"
10 #include "../common/fe.h"
11 #include "../common/util.h"
12 #include "../common/outbound.h"
20 #include <libnotify/notify.h>
21 #ifndef NOTIFY_CHECK_VERSION
22 #define NOTIFY_CHECK_VERSION(x,y,z) 0
24 #if NOTIFY_CHECK_VERSION(0,7,0)
25 #define XC_NOTIFY_NEW(a,b,c,d) notify_notification_new(a,b,c)
27 #define XC_NOTIFY_NEW(a,b,c,d) notify_notification_new(a,b,c,d)
31 typedef enum /* current icon status */
37 TS_CUSTOM
/* plugin */
47 typedef GdkPixbuf
* TrayIcon
;
48 #define tray_icon_from_file(f) gdk_pixbuf_new_from_file(f,NULL)
49 #define tray_icon_free(i) g_object_unref(i)
51 #define ICON_NORMAL pix_xchat
52 #define ICON_MSG pix_tray_msg
53 #define ICON_HILIGHT pix_tray_hilight
54 #define ICON_FILE pix_tray_file
57 static GtkStatusIcon
*sticon
;
58 static gint flash_tag
;
59 static TrayStatus tray_status
;
60 static xchat_plugin
*ph
;
62 static TrayIcon custom_icon1
;
63 static TrayIcon custom_icon2
;
65 static int tray_priv_count
= 0;
66 static int tray_pub_count
= 0;
67 static int tray_hilight_count
= 0;
68 static int tray_file_count
= 0;
71 void tray_apply_setup (void);
75 tray_get_window_status (void)
79 st
= xchat_get_info (ph
, "win_status");
84 if (!strcmp (st
, "active"))
87 if (!strcmp (st
, "hidden"))
94 tray_count_channels (void)
100 for (list
= sess_list
; list
; list
= list
->next
)
103 if (sess
->server
->connected
&& sess
->channel
[0] &&
104 sess
->type
== SESS_CHANNEL
)
111 tray_count_networks (void)
116 for (list
= serv_list
; list
; list
= list
->next
)
118 if (((server
*)list
->data
)->connected
)
125 fe_tray_set_tooltip (const char *text
)
128 gtk_status_icon_set_tooltip (sticon
, text
);
132 fe_tray_set_balloon (const char *title
, const char *text
)
139 /* no balloons if the window is focused */
140 ws
= tray_get_window_status ();
141 if (ws
== WS_FOCUSED
)
144 /* bit 1 of flags means "no balloons unless hidden/iconified" */
145 if (ws
!= WS_HIDDEN
&& (prefs
.gui_tray_flags
& 2))
148 /* FIXME: this should close the current balloon */
153 NotifyNotification
*notification
;
154 char *notify_text
, *notify_title
;
156 if (!notify_is_initted())
157 notify_init(PACKAGE_NAME
);
159 notify_text
= strip_color (text
, -1, STRIP_ALL
|STRIP_ESCMARKUP
);
160 notify_title
= strip_color (title
, -1, STRIP_ALL
);
162 notification
= XC_NOTIFY_NEW (notify_title
, notify_text
, XCHATSHAREDIR
"/pixmaps/xchat.png", NULL
);
164 g_free ((char *)notify_title
);
165 g_free ((char *)notify_text
);
167 notify_notification_set_timeout (notification
, prefs
.input_balloon_time
*1000);
168 notify_notification_show (notification
, NULL
);
170 g_object_unref (notification
);
175 tray_set_balloonf (const char *text
, const char *format
, ...)
180 va_start (args
, format
);
181 buf
= g_strdup_vprintf (format
, args
);
184 fe_tray_set_balloon (buf
, text
);
189 tray_set_tipf (const char *format
, ...)
194 va_start (args
, format
);
195 buf
= g_strdup_vprintf (format
, args
);
198 fe_tray_set_tooltip (buf
);
203 tray_stop_flash (void)
209 g_source_remove (flash_tag
);
215 gtk_status_icon_set_from_pixbuf (sticon
, ICON_NORMAL
);
216 nets
= tray_count_networks ();
217 chans
= tray_count_channels ();
219 tray_set_tipf (_("XChat: Connected to %u networks and %u channels"),
222 tray_set_tipf ("XChat: %s", _("Not connected."));
227 tray_icon_free (custom_icon1
);
233 tray_icon_free (custom_icon2
);
237 tray_status
= TS_NONE
;
241 tray_reset_counts (void)
245 tray_hilight_count
= 0;
250 tray_timeout_cb (TrayIcon icon
)
254 if (gtk_status_icon_get_pixbuf (sticon
) == custom_icon1
)
257 gtk_status_icon_set_from_pixbuf (sticon
, custom_icon2
);
259 gtk_status_icon_set_from_pixbuf (sticon
, ICON_NORMAL
);
263 gtk_status_icon_set_from_pixbuf (sticon
, custom_icon1
);
268 if (gtk_status_icon_get_pixbuf (sticon
) == ICON_NORMAL
)
269 gtk_status_icon_set_from_pixbuf (sticon
, icon
);
271 gtk_status_icon_set_from_pixbuf (sticon
, ICON_NORMAL
);
277 tray_set_flash (TrayIcon icon
)
282 /* already flashing the same icon */
283 if (flash_tag
&& gtk_status_icon_get_pixbuf (sticon
) == icon
)
286 /* no flashing if window is focused */
287 if (tray_get_window_status () == WS_FOCUSED
)
292 gtk_status_icon_set_from_pixbuf (sticon
, icon
);
293 flash_tag
= g_timeout_add (TIMEOUT
, (GSourceFunc
) tray_timeout_cb
, icon
);
297 fe_tray_set_flash (const char *filename1
, const char *filename2
, int tout
)
308 custom_icon1
= tray_icon_from_file (filename1
);
310 custom_icon2
= tray_icon_from_file (filename2
);
312 gtk_status_icon_set_from_pixbuf (sticon
, custom_icon1
);
313 flash_tag
= g_timeout_add (tout
, (GSourceFunc
) tray_timeout_cb
, NULL
);
314 tray_status
= TS_CUSTOM
;
318 fe_tray_set_icon (feicon icon
)
330 case FE_ICON_MESSAGE
:
331 tray_set_flash (ICON_MSG
);
333 case FE_ICON_HIGHLIGHT
:
334 case FE_ICON_PRIVMSG
:
335 tray_set_flash (ICON_HILIGHT
);
337 case FE_ICON_FILEOFFER
:
338 tray_set_flash (ICON_FILE
);
343 fe_tray_set_file (const char *filename
)
353 custom_icon1
= tray_icon_from_file (filename
);
354 gtk_status_icon_set_from_pixbuf (sticon
, custom_icon1
);
355 tray_status
= TS_CUSTOM
;
360 tray_toggle_visibility (gboolean force_hide
)
363 static GdkScreen
*screen
;
369 /* ph may have an invalid context now */
370 xchat_set_context (ph
, xchat_find_context (ph
, NULL
, NULL
));
372 win
= (GtkWindow
*)xchat_get_info (ph
, "win_ptr");
375 tray_reset_counts ();
380 #if GTK_CHECK_VERSION(2,20,0)
381 if (force_hide
|| gtk_widget_get_visible (GTK_WIDGET (win
)))
383 if (force_hide
|| GTK_WIDGET_VISIBLE (win
))
386 gtk_window_get_position (win
, &x
, &y
);
387 screen
= gtk_window_get_screen (win
);
388 gtk_widget_hide (GTK_WIDGET (win
));
392 gtk_window_set_screen (win
, screen
);
393 gtk_window_move (win
, x
, y
);
394 gtk_widget_show (GTK_WIDGET (win
));
395 gtk_window_present (win
);
402 tray_menu_restore_cb (GtkWidget
*item
, gpointer userdata
)
404 tray_toggle_visibility (FALSE
);
408 tray_menu_quit_cb (GtkWidget
*item
, gpointer userdata
)
410 mg_open_quit_dialog (FALSE
);
413 /* returns 0-mixed 1-away 2-back */
416 tray_find_away_status (void)
423 for (list
= serv_list
; list
; list
= list
->next
)
427 if (serv
->is_away
|| serv
->reconnect_away
)
443 tray_foreach_server (GtkWidget
*item
, char *cmd
)
448 for (list
= serv_list
; list
; list
= list
->next
)
452 handle_command (serv
->server_session
, cmd
, FALSE
);
457 tray_make_item (GtkWidget
*menu
, char *label
, void *callback
, void *userdata
)
462 item
= gtk_menu_item_new_with_mnemonic (label
);
464 item
= gtk_menu_item_new ();
465 gtk_menu_shell_append (GTK_MENU_SHELL (menu
), item
);
466 g_signal_connect (G_OBJECT (item
), "activate",
467 G_CALLBACK (callback
), userdata
);
468 gtk_widget_show (item
);
474 tray_toggle_cb (GtkCheckMenuItem
*item
, unsigned int *setting
)
476 *setting
= item
->active
;
480 blink_item (unsigned int *setting
, GtkWidget
*menu
, char *label
)
482 menu_toggle_item (label
, menu
, tray_toggle_cb
, setting
, *setting
);
486 tray_menu_destroy (GtkWidget
*menu
, gpointer userdata
)
488 gtk_widget_destroy (menu
);
489 g_object_unref (menu
);
493 tray_menu_cb (GtkWidget
*widget
, guint button
, guint time
, gpointer userdata
)
500 /* ph may have an invalid context now */
501 xchat_set_context (ph
, xchat_find_context (ph
, NULL
, NULL
));
503 menu
= gtk_menu_new ();
504 /*gtk_menu_set_screen (GTK_MENU (menu), gtk_widget_get_screen (widget));*/
506 if (tray_get_window_status () == WS_HIDDEN
)
507 tray_make_item (menu
, _("_Restore"), tray_menu_restore_cb
, NULL
);
509 tray_make_item (menu
, _("_Hide"), tray_menu_restore_cb
, NULL
);
510 tray_make_item (menu
, NULL
, tray_menu_quit_cb
, NULL
);
512 submenu
= mg_submenu (menu
, _("_Blink on"));
513 blink_item (&prefs
.input_tray_chans
, submenu
, _("Channel Message"));
514 blink_item (&prefs
.input_tray_priv
, submenu
, _("Private Message"));
515 blink_item (&prefs
.input_tray_hilight
, submenu
, _("Highlighted Message"));
516 /*blink_item (BIT_FILEOFFER, submenu, _("File Offer"));*/
518 submenu
= mg_submenu (menu
, _("_Change status"));
519 away_status
= tray_find_away_status ();
520 item
= tray_make_item (submenu
, _("_Away"), tray_foreach_server
, "away");
521 if (away_status
== 1)
522 gtk_widget_set_sensitive (item
, FALSE
);
523 item
= tray_make_item (submenu
, _("_Back"), tray_foreach_server
, "back");
524 if (away_status
== 2)
525 gtk_widget_set_sensitive (item
, FALSE
);
527 tray_make_item (menu
, NULL
, tray_menu_quit_cb
, NULL
);
528 mg_create_icon_item (_("_Quit"), GTK_STOCK_QUIT
, menu
, tray_menu_quit_cb
, NULL
);
530 menu_add_plugin_items (menu
, "\x5$TRAY", NULL
);
533 g_object_ref_sink (menu
);
534 g_object_unref (menu
);
535 g_signal_connect (G_OBJECT (menu
), "selection-done",
536 G_CALLBACK (tray_menu_destroy
), NULL
);
538 gtk_menu_popup (GTK_MENU (menu
), NULL
, NULL
, gtk_status_icon_position_menu
,
539 userdata
, button
, time
);
546 tray_status
= TS_NONE
;
550 sticon
= gtk_status_icon_new_from_pixbuf (ICON_NORMAL
);
553 g_signal_connect (G_OBJECT (sticon
), "popup-menu",
554 G_CALLBACK (tray_menu_cb
), sticon
);
555 g_signal_connect (G_OBJECT (sticon
), "activate",
556 G_CALLBACK (tray_menu_restore_cb
), NULL
);
560 tray_hilight_cb (char *word
[], void *userdata
)
562 /*if (tray_status == TS_HIGHLIGHT)
563 return XCHAT_EAT_NONE;*/
565 if (prefs
.input_tray_hilight
)
567 tray_set_flash (ICON_HILIGHT
);
569 /* FIXME: hides any previous private messages */
570 tray_hilight_count
++;
571 if (tray_hilight_count
== 1)
572 tray_set_tipf (_("XChat: Highlighted message from: %s (%s)"),
573 word
[1], xchat_get_info (ph
, "channel"));
575 tray_set_tipf (_("XChat: %u highlighted messages, latest from: %s (%s)"),
576 tray_hilight_count
, word
[1], xchat_get_info (ph
, "channel"));
579 if (prefs
.input_balloon_hilight
)
580 tray_set_balloonf (word
[2], _("XChat: Highlighted message from: %s (%s)"),
581 word
[1], xchat_get_info (ph
, "channel"));
583 return XCHAT_EAT_NONE
;
587 tray_message_cb (char *word
[], void *userdata
)
589 if (/*tray_status == TS_MESSAGE ||*/ tray_status
== TS_HIGHLIGHT
)
590 return XCHAT_EAT_NONE
;
592 if (prefs
.input_tray_chans
)
594 tray_set_flash (ICON_MSG
);
597 if (tray_pub_count
== 1)
598 tray_set_tipf (_("XChat: New public message from: %s (%s)"),
599 word
[1], xchat_get_info (ph
, "channel"));
601 tray_set_tipf (_("XChat: %u new public messages."), tray_pub_count
);
604 if (prefs
.input_balloon_chans
)
605 tray_set_balloonf (word
[2], _("XChat: New public message from: %s (%s)"),
606 word
[1], xchat_get_info (ph
, "channel"));
608 return XCHAT_EAT_NONE
;
612 tray_priv (char *from
, char *text
)
616 if (alert_match_word (from
, prefs
.irc_no_hilight
))
619 tray_set_flash (ICON_HILIGHT
);
621 network
= xchat_get_info (ph
, "network");
623 network
= xchat_get_info (ph
, "server");
626 if (tray_priv_count
== 1)
627 tray_set_tipf (_("XChat: Private message from: %s (%s)"),
630 tray_set_tipf (_("XChat: %u private messages, latest from: %s (%s)"),
631 tray_priv_count
, from
, network
);
633 if (prefs
.input_balloon_priv
)
634 tray_set_balloonf (text
, _("XChat: Private message from: %s (%s)"),
639 tray_priv_cb (char *word
[], void *userdata
)
641 /*if (tray_status == TS_HIGHLIGHT)
642 return XCHAT_EAT_NONE;*/
644 if (prefs
.input_tray_priv
)
645 tray_priv (word
[1], word
[2]);
647 return XCHAT_EAT_NONE
;
651 tray_invited_cb (char *word
[], void *userdata
)
653 /*if (tray_status == TS_HIGHLIGHT)
654 return XCHAT_EAT_NONE;*/
656 if (prefs
.input_tray_priv
)
657 tray_priv (word
[2], "Invited");
659 return XCHAT_EAT_NONE
;
663 tray_dcc_cb (char *word
[], void *userdata
)
667 /* if (tray_status == TS_FILEOFFER)
668 return XCHAT_EAT_NONE;*/
670 network
= xchat_get_info (ph
, "network");
672 network
= xchat_get_info (ph
, "server");
674 if (prefs
.input_tray_priv
)
676 tray_set_flash (ICON_FILE
);
679 if (tray_file_count
== 1)
680 tray_set_tipf (_("XChat: File offer from: %s (%s)"),
683 tray_set_tipf (_("XChat: %u file offers, latest from: %s (%s)"),
684 tray_file_count
, word
[1], network
);
687 if (prefs
.input_balloon_priv
)
688 tray_set_balloonf ("", _("XChat: File offer from: %s (%s)"),
691 return XCHAT_EAT_NONE
;
695 tray_focus_cb (char *word
[], void *userdata
)
698 tray_reset_counts ();
699 return XCHAT_EAT_NONE
;
709 g_object_unref ((GObject
*)sticon
);
715 tray_apply_setup (void)
730 tray_plugin_init (xchat_plugin
*plugin_handle
, char **plugin_name
,
731 char **plugin_desc
, char **plugin_version
, char *arg
)
733 /* we need to save this for use with any xchat_* functions */
738 *plugin_version
= "";
740 xchat_hook_print (ph
, "Channel Msg Hilight", -1, tray_hilight_cb
, NULL
);
741 xchat_hook_print (ph
, "Channel Action Hilight", -1, tray_hilight_cb
, NULL
);
743 xchat_hook_print (ph
, "Channel Message", -1, tray_message_cb
, NULL
);
744 xchat_hook_print (ph
, "Channel Action", -1, tray_message_cb
, NULL
);
745 xchat_hook_print (ph
, "Channel Notice", -1, tray_message_cb
, NULL
);
747 xchat_hook_print (ph
, "Private Message", -1, tray_priv_cb
, NULL
);
748 xchat_hook_print (ph
, "Private Message to Dialog", -1, tray_priv_cb
, NULL
);
749 xchat_hook_print (ph
, "Notice", -1, tray_priv_cb
, NULL
);
750 xchat_hook_print (ph
, "Invited", -1, tray_invited_cb
, NULL
);
752 xchat_hook_print (ph
, "DCC Offer", -1, tray_dcc_cb
, NULL
);
754 xchat_hook_print (ph
, "Focus Window", -1, tray_focus_cb
, NULL
);
759 return 1; /* return 1 for success */
763 tray_plugin_deinit (xchat_plugin
*plugin_handle
)
765 #if defined(USE_LIBNOTIFY)