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"
20
typedef enum /* current icon status */
26
TS_CUSTOM /* plugin */
36
typedef GdkPixbuf* TrayIcon;
37
#define tray_icon_from_file(f) gdk_pixbuf_new_from_file(f,NULL)
38
#define tray_icon_free(i) g_object_unref(i)
40
#define ICON_NORMAL pix_xchat
41
#define ICON_MSG pix_tray_msg
42
#define ICON_HILIGHT pix_tray_hilight
43
#define ICON_FILE pix_tray_file
46
static GtkStatusIcon *sticon;
47
static gint flash_tag;
48
static TrayStatus tray_status;
49
static xchat_plugin *ph;
51
static TrayIcon custom_icon1;
52
static TrayIcon custom_icon2;
54
static int tray_priv_count = 0;
55
static int tray_pub_count = 0;
56
static int tray_hilight_count = 0;
57
static int tray_file_count = 0;
60
void tray_apply_setup (void);
64
tray_get_window_status (void)
68
st = xchat_get_info (ph, "win_status");
73
if (!strcmp (st, "active"))
76
if (!strcmp (st, "hidden"))
83
tray_count_channels (void)
89
for (list = sess_list; list; list = list->next)
92
if (sess->server->connected && sess->channel[0] &&
93
sess->type == SESS_CHANNEL)
100
tray_count_networks (void)
105
for (list = serv_list; list; list = list->next)
107
if (((server *)list->data)->connected)
114
fe_tray_set_tooltip (const char *text)
117
gtk_status_icon_set_tooltip (sticon, text);
122
/* dynamic access to libnotify.so */
124
static void *nn_mod = NULL;
126
static gboolean (*nn_init) (char *);
127
static void (*nn_uninit) (void);
128
static void *(*nn_new_with_status_icon) (const gchar *summary, const gchar *message, const gchar *icon, GtkStatusIcon *status_icon);
129
static void *(*nn_new) (const gchar *summary, const gchar *message, const gchar *icon, GtkWidget *attach);
130
static gboolean (*nn_show) (void *noti, GError **error);
131
static void (*nn_set_timeout) (void *noti, gint timeout);
134
libnotify_cleanup (void)
139
g_module_close (nn_mod);
145
libnotify_notify_new (const char *title, const char *text, GtkStatusIcon *icon)
151
nn_mod = g_module_open ("libnotify", G_MODULE_BIND_LAZY);
154
nn_mod = g_module_open ("libnotify.so.1", G_MODULE_BIND_LAZY);
159
if (!g_module_symbol (nn_mod, "notify_init", (gpointer)&nn_init))
161
if (!g_module_symbol (nn_mod, "notify_uninit", (gpointer)&nn_uninit))
163
if (!g_module_symbol (nn_mod, "notify_notification_new_with_status_icon", (gpointer)&nn_new_with_status_icon))
165
if (!g_module_symbol (nn_mod, "notify_notification_new", (gpointer)&nn_new))
167
if (!g_module_symbol (nn_mod, "notify_notification_show", (gpointer)&nn_show))
169
if (!g_module_symbol (nn_mod, "notify_notification_set_timeout", (gpointer)&nn_set_timeout))
171
if (!nn_init (PACKAGE_NAME))
175
text = strip_color (text, -1, STRIP_ALL|STRIP_ESCMARKUP);
176
title = strip_color (title, -1, STRIP_ALL);
177
noti = nn_new (title, text, XCHATSHAREDIR"/pixmaps/xchat.png", NULL);
178
g_free ((char *)title);
179
g_free ((char *)text);
181
nn_set_timeout (noti, prefs.input_balloon_time*1000);
182
nn_show (noti, NULL);
183
g_object_unref (G_OBJECT (noti));
188
g_module_close (nn_mod);
196
fe_tray_set_balloon (const char *title, const char *text)
204
/* no balloons if the window is focused */
205
ws = tray_get_window_status ();
206
if (ws == WS_FOCUSED)
209
/* bit 1 of flags means "no balloons unless hidden/iconified" */
210
if (ws != WS_HIDDEN && (prefs.gui_tray_flags & 2))
213
/* FIXME: this should close the current balloon */
218
/* try it via libnotify.so */
219
if (libnotify_notify_new (title, text, sticon))
220
return; /* success */
223
/* try it the crude way */
224
path = g_find_program_in_path ("notify-send");
227
sprintf(time, "%d000",prefs.input_balloon_time);
230
argv[2] = "gtk-dialog-info";
231
if (access (XCHATSHAREDIR"/pixmaps/xchat.png", R_OK) == 0)
232
argv[2] = XCHATSHAREDIR"/pixmaps/xchat.png";
236
text = strip_color (text, -1, STRIP_ALL|STRIP_ESCMARKUP);
240
g_free ((char *)path);
241
g_free ((char *)text);
245
/* show this error only once */
246
static unsigned char said_it = FALSE;
250
fe_message (_("Cannot find 'notify-send' to open balloon alerts.\nPlease install libnotify."), FE_MSG_ERROR);
257
tray_set_balloonf (const char *text, const char *format, ...)
262
va_start (args, format);
263
buf = g_strdup_vprintf (format, args);
266
fe_tray_set_balloon (buf, text);
271
tray_set_tipf (const char *format, ...)
276
va_start (args, format);
277
buf = g_strdup_vprintf (format, args);
280
fe_tray_set_tooltip (buf);
285
tray_stop_flash (void)
291
g_source_remove (flash_tag);
297
gtk_status_icon_set_from_pixbuf (sticon, ICON_NORMAL);
298
nets = tray_count_networks ();
299
chans = tray_count_channels ();
301
tray_set_tipf (_("XChat: Connected to %u networks and %u channels"),
304
tray_set_tipf ("XChat: %s", _("Not connected."));
309
tray_icon_free (custom_icon1);
315
tray_icon_free (custom_icon2);
319
tray_status = TS_NONE;
323
tray_reset_counts (void)
327
tray_hilight_count = 0;
332
tray_timeout_cb (TrayIcon icon)
336
if (gtk_status_icon_get_pixbuf (sticon) == custom_icon1)
339
gtk_status_icon_set_from_pixbuf (sticon, custom_icon2);
341
gtk_status_icon_set_from_pixbuf (sticon, ICON_NORMAL);
345
gtk_status_icon_set_from_pixbuf (sticon, custom_icon1);
350
if (gtk_status_icon_get_pixbuf (sticon) == ICON_NORMAL)
351
gtk_status_icon_set_from_pixbuf (sticon, icon);
353
gtk_status_icon_set_from_pixbuf (sticon, ICON_NORMAL);
359
tray_set_flash (TrayIcon icon)
364
/* already flashing the same icon */
365
if (flash_tag && gtk_status_icon_get_pixbuf (sticon) == icon)
368
/* no flashing if window is focused */
369
if (tray_get_window_status () == WS_FOCUSED)
374
gtk_status_icon_set_from_pixbuf (sticon, icon);
375
flash_tag = g_timeout_add (TIMEOUT, (GSourceFunc) tray_timeout_cb, icon);
379
fe_tray_set_flash (const char *filename1, const char *filename2, int tout)
390
custom_icon1 = tray_icon_from_file (filename1);
392
custom_icon2 = tray_icon_from_file (filename2);
394
gtk_status_icon_set_from_pixbuf (sticon, custom_icon1);
395
flash_tag = g_timeout_add (tout, (GSourceFunc) tray_timeout_cb, NULL);
396
tray_status = TS_CUSTOM;
400
fe_tray_set_icon (feicon icon)
412
case FE_ICON_MESSAGE:
413
tray_set_flash (ICON_MSG);
415
case FE_ICON_HIGHLIGHT:
416
case FE_ICON_PRIVMSG:
417
tray_set_flash (ICON_HILIGHT);
419
case FE_ICON_FILEOFFER:
420
tray_set_flash (ICON_FILE);
425
fe_tray_set_file (const char *filename)
435
custom_icon1 = tray_icon_from_file (filename);
436
gtk_status_icon_set_from_pixbuf (sticon, custom_icon1);
437
tray_status = TS_CUSTOM;
442
tray_toggle_visibility (gboolean force_hide)
445
static GdkScreen *screen;
451
/* ph may have an invalid context now */
452
xchat_set_context (ph, xchat_find_context (ph, NULL, NULL));
454
win = (GtkWindow *)xchat_get_info (ph, "win_ptr");
457
tray_reset_counts ();
462
#if GTK_CHECK_VERSION(2,20,0)
463
if (force_hide || gtk_widget_get_visible (win))
465
if (force_hide || GTK_WIDGET_VISIBLE (win))
468
gtk_window_get_position (win, &x, &y);
469
screen = gtk_window_get_screen (win);
470
gtk_widget_hide (GTK_WIDGET (win));
474
gtk_window_set_screen (win, screen);
475
gtk_window_move (win, x, y);
476
gtk_widget_show (GTK_WIDGET (win));
477
gtk_window_present (win);
484
tray_menu_restore_cb (GtkWidget *item, gpointer userdata)
486
tray_toggle_visibility (FALSE);
490
tray_menu_quit_cb (GtkWidget *item, gpointer userdata)
492
mg_open_quit_dialog (FALSE);
495
/* returns 0-mixed 1-away 2-back */
498
tray_find_away_status (void)
505
for (list = serv_list; list; list = list->next)
509
if (serv->is_away || serv->reconnect_away)
525
tray_foreach_server (GtkWidget *item, char *cmd)
530
for (list = serv_list; list; list = list->next)
534
handle_command (serv->server_session, cmd, FALSE);
539
tray_make_item (GtkWidget *menu, char *label, void *callback, void *userdata)
544
item = gtk_menu_item_new_with_mnemonic (label);
546
item = gtk_menu_item_new ();
547
gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
548
g_signal_connect (G_OBJECT (item), "activate",
549
G_CALLBACK (callback), userdata);
550
gtk_widget_show (item);
556
tray_toggle_cb (GtkCheckMenuItem *item, unsigned int *setting)
558
*setting = item->active;
562
blink_item (unsigned int *setting, GtkWidget *menu, char *label)
564
menu_toggle_item (label, menu, tray_toggle_cb, setting, *setting);
568
tray_menu_destroy (GtkWidget *menu, gpointer userdata)
570
gtk_widget_destroy (menu);
571
g_object_unref (menu);
575
tray_menu_cb (GtkWidget *widget, guint button, guint time, gpointer userdata)
582
/* ph may have an invalid context now */
583
xchat_set_context (ph, xchat_find_context (ph, NULL, NULL));
585
menu = gtk_menu_new ();
586
/*gtk_menu_set_screen (GTK_MENU (menu), gtk_widget_get_screen (widget));*/
588
if (tray_get_window_status () == WS_HIDDEN)
589
tray_make_item (menu, _("_Restore"), tray_menu_restore_cb, NULL);
591
tray_make_item (menu, _("_Hide"), tray_menu_restore_cb, NULL);
592
tray_make_item (menu, NULL, tray_menu_quit_cb, NULL);
594
submenu = mg_submenu (menu, _("_Blink on"));
595
blink_item (&prefs.input_tray_chans, submenu, _("Channel Message"));
596
blink_item (&prefs.input_tray_priv, submenu, _("Private Message"));
597
blink_item (&prefs.input_tray_hilight, submenu, _("Highlighted Message"));
598
/*blink_item (BIT_FILEOFFER, submenu, _("File Offer"));*/
600
submenu = mg_submenu (menu, _("_Change status"));
601
away_status = tray_find_away_status ();
602
item = tray_make_item (submenu, _("_Away"), tray_foreach_server, "away");
603
if (away_status == 1)
604
gtk_widget_set_sensitive (item, FALSE);
605
item = tray_make_item (submenu, _("_Back"), tray_foreach_server, "back");
606
if (away_status == 2)
607
gtk_widget_set_sensitive (item, FALSE);
609
tray_make_item (menu, NULL, tray_menu_quit_cb, NULL);
610
mg_create_icon_item (_("_Quit"), GTK_STOCK_QUIT, menu, tray_menu_quit_cb, NULL);
612
menu_add_plugin_items (menu, "\x5$TRAY", NULL);
615
g_object_ref_sink (menu);
616
g_object_unref (menu);
617
g_signal_connect (G_OBJECT (menu), "selection-done",
618
G_CALLBACK (tray_menu_destroy), NULL);
620
gtk_menu_popup (GTK_MENU (menu), NULL, NULL, gtk_status_icon_position_menu,
621
userdata, button, time);
628
tray_status = TS_NONE;
632
sticon = gtk_status_icon_new_from_pixbuf (ICON_NORMAL);
635
g_signal_connect (G_OBJECT (sticon), "popup-menu",
636
G_CALLBACK (tray_menu_cb), sticon);
637
g_signal_connect (G_OBJECT (sticon), "activate",
638
G_CALLBACK (tray_menu_restore_cb), NULL);
642
tray_hilight_cb (char *word[], void *userdata)
644
/*if (tray_status == TS_HIGHLIGHT)
645
return XCHAT_EAT_NONE;*/
647
if (prefs.input_tray_hilight)
649
tray_set_flash (ICON_HILIGHT);
651
/* FIXME: hides any previous private messages */
652
tray_hilight_count++;
653
if (tray_hilight_count == 1)
654
tray_set_tipf (_("XChat: Highlighted message from: %s (%s)"),
655
word[1], xchat_get_info (ph, "channel"));
657
tray_set_tipf (_("XChat: %u highlighted messages, latest from: %s (%s)"),
658
tray_hilight_count, word[1], xchat_get_info (ph, "channel"));
661
if (prefs.input_balloon_hilight)
662
tray_set_balloonf (word[2], _("XChat: Highlighted message from: %s (%s)"),
663
word[1], xchat_get_info (ph, "channel"));
665
return XCHAT_EAT_NONE;
669
tray_message_cb (char *word[], void *userdata)
671
if (/*tray_status == TS_MESSAGE ||*/ tray_status == TS_HIGHLIGHT)
672
return XCHAT_EAT_NONE;
674
if (prefs.input_tray_chans)
676
tray_set_flash (ICON_MSG);
679
if (tray_pub_count == 1)
680
tray_set_tipf (_("XChat: New public message from: %s (%s)"),
681
word[1], xchat_get_info (ph, "channel"));
683
tray_set_tipf (_("XChat: %u new public messages."), tray_pub_count);
686
if (prefs.input_balloon_chans)
687
tray_set_balloonf (word[2], _("XChat: New public message from: %s (%s)"),
688
word[1], xchat_get_info (ph, "channel"));
690
return XCHAT_EAT_NONE;
694
tray_priv (char *from, char *text)
698
if (alert_match_word (from, prefs.irc_no_hilight))
701
tray_set_flash (ICON_HILIGHT);
703
network = xchat_get_info (ph, "network");
705
network = xchat_get_info (ph, "server");
708
if (tray_priv_count == 1)
709
tray_set_tipf (_("XChat: Private message from: %s (%s)"),
712
tray_set_tipf (_("XChat: %u private messages, latest from: %s (%s)"),
713
tray_priv_count, from, network);
715
if (prefs.input_balloon_priv)
716
tray_set_balloonf (text, _("XChat: Private message from: %s (%s)"),
721
tray_priv_cb (char *word[], void *userdata)
723
/*if (tray_status == TS_HIGHLIGHT)
724
return XCHAT_EAT_NONE;*/
726
if (prefs.input_tray_priv)
727
tray_priv (word[1], word[2]);
729
return XCHAT_EAT_NONE;
733
tray_invited_cb (char *word[], void *userdata)
735
/*if (tray_status == TS_HIGHLIGHT)
736
return XCHAT_EAT_NONE;*/
738
if (prefs.input_tray_priv)
739
tray_priv (word[2], "Invited");
741
return XCHAT_EAT_NONE;
745
tray_dcc_cb (char *word[], void *userdata)
749
/* if (tray_status == TS_FILEOFFER)
750
return XCHAT_EAT_NONE;*/
752
network = xchat_get_info (ph, "network");
754
network = xchat_get_info (ph, "server");
756
if (prefs.input_tray_priv)
758
tray_set_flash (ICON_FILE);
761
if (tray_file_count == 1)
762
tray_set_tipf (_("XChat: File offer from: %s (%s)"),
765
tray_set_tipf (_("XChat: %u file offers, latest from: %s (%s)"),
766
tray_file_count, word[1], network);
769
if (prefs.input_balloon_priv)
770
tray_set_balloonf ("", _("XChat: File offer from: %s (%s)"),
773
return XCHAT_EAT_NONE;
777
tray_focus_cb (char *word[], void *userdata)
780
tray_reset_counts ();
781
return XCHAT_EAT_NONE;
791
g_object_unref ((GObject *)sticon);
797
tray_apply_setup (void)
812
tray_plugin_init (xchat_plugin *plugin_handle, char **plugin_name,
813
char **plugin_desc, char **plugin_version, char *arg)
815
/* we need to save this for use with any xchat_* functions */
820
*plugin_version = "";
822
xchat_hook_print (ph, "Channel Msg Hilight", -1, tray_hilight_cb, NULL);
823
xchat_hook_print (ph, "Channel Action Hilight", -1, tray_hilight_cb, NULL);
825
xchat_hook_print (ph, "Channel Message", -1, tray_message_cb, NULL);
826
xchat_hook_print (ph, "Channel Action", -1, tray_message_cb, NULL);
827
xchat_hook_print (ph, "Channel Notice", -1, tray_message_cb, NULL);
829
xchat_hook_print (ph, "Private Message", -1, tray_priv_cb, NULL);
830
xchat_hook_print (ph, "Private Message to Dialog", -1, tray_priv_cb, NULL);
831
xchat_hook_print (ph, "Notice", -1, tray_priv_cb, NULL);
832
xchat_hook_print (ph, "Invited", -1, tray_invited_cb, NULL);
834
xchat_hook_print (ph, "DCC Offer", -1, tray_dcc_cb, NULL);
836
xchat_hook_print (ph, "Focus Window", -1, tray_focus_cb, NULL);
841
return 1; /* return 1 for success */
845
tray_plugin_deinit (xchat_plugin *plugin_handle)
849
#elif defined(LIBNOTIFY)
850
libnotify_cleanup ();