fix implicit declarations
[rofl0r-ixchat.git] / src / fe-gtk / plugin-tray.c
blob1b7475ad5e14554382a969ed16333502e2969ade
1 /* Copyright (C) 2006-2007 Peter Zelezny. */
3 #include <string.h>
4 #include <unistd.h>
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"
13 #include "fe-gtk.h"
14 #include "pixmaps.h"
15 #include "maingui.h"
16 #include "menu.h"
17 #include <gtk/gtk.h>
19 #ifdef USE_LIBNOTIFY
20 #include <libnotify/notify.h>
21 #ifndef NOTIFY_CHECK_VERSION
22 #define NOTIFY_CHECK_VERSION(x,y,z) 0
23 #endif
24 #if NOTIFY_CHECK_VERSION(0,7,0)
25 #define XC_NOTIFY_NEW(a,b,c,d) notify_notification_new(a,b,c)
26 #else
27 #define XC_NOTIFY_NEW(a,b,c,d) notify_notification_new(a,b,c,d)
28 #endif
29 #endif
31 typedef enum /* current icon status */
33 TS_NONE,
34 TS_MESSAGE,
35 TS_HIGHLIGHT,
36 TS_FILEOFFER,
37 TS_CUSTOM /* plugin */
38 } TrayStatus;
40 typedef enum
42 WS_FOCUSED,
43 WS_NORMAL,
44 WS_HIDDEN
45 } WinStatus;
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
55 #define TIMEOUT 500
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);
74 static WinStatus
75 tray_get_window_status (void)
77 const char *st;
79 st = xchat_get_info (ph, "win_status");
81 if (!st)
82 return WS_HIDDEN;
84 if (!strcmp (st, "active"))
85 return WS_FOCUSED;
87 if (!strcmp (st, "hidden"))
88 return WS_HIDDEN;
90 return WS_NORMAL;
93 static int
94 tray_count_channels (void)
96 int cons = 0;
97 GSList *list;
98 session *sess;
100 for (list = sess_list; list; list = list->next)
102 sess = list->data;
103 if (sess->server->connected && sess->channel[0] &&
104 sess->type == SESS_CHANNEL)
105 cons++;
107 return cons;
110 static int
111 tray_count_networks (void)
113 int cons = 0;
114 GSList *list;
116 for (list = serv_list; list; list = list->next)
118 if (((server *)list->data)->connected)
119 cons++;
121 return cons;
124 void
125 fe_tray_set_tooltip (const char *text)
127 if (sticon)
128 gtk_status_icon_set_tooltip (sticon, text);
131 void
132 fe_tray_set_balloon (const char *title, const char *text)
134 const char *argv[8];
135 const char *path;
136 char time[16];
137 WinStatus ws;
139 /* no balloons if the window is focused */
140 ws = tray_get_window_status ();
141 if (ws == WS_FOCUSED)
142 return;
144 /* bit 1 of flags means "no balloons unless hidden/iconified" */
145 if (ws != WS_HIDDEN && (prefs.gui_tray_flags & 2))
146 return;
148 /* FIXME: this should close the current balloon */
149 if (!text)
150 return;
152 #ifdef USE_LIBNOTIFY
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);
171 #endif
174 static void
175 tray_set_balloonf (const char *text, const char *format, ...)
177 va_list args;
178 char *buf;
180 va_start (args, format);
181 buf = g_strdup_vprintf (format, args);
182 va_end (args);
184 fe_tray_set_balloon (buf, text);
185 g_free (buf);
188 static void
189 tray_set_tipf (const char *format, ...)
191 va_list args;
192 char *buf;
194 va_start (args, format);
195 buf = g_strdup_vprintf (format, args);
196 va_end (args);
198 fe_tray_set_tooltip (buf);
199 g_free (buf);
202 static void
203 tray_stop_flash (void)
205 int nets, chans;
207 if (flash_tag)
209 g_source_remove (flash_tag);
210 flash_tag = 0;
213 if (sticon)
215 gtk_status_icon_set_from_pixbuf (sticon, ICON_NORMAL);
216 nets = tray_count_networks ();
217 chans = tray_count_channels ();
218 if (nets)
219 tray_set_tipf (_("XChat: Connected to %u networks and %u channels"),
220 nets, chans);
221 else
222 tray_set_tipf ("XChat: %s", _("Not connected."));
225 if (custom_icon1)
227 tray_icon_free (custom_icon1);
228 custom_icon1 = NULL;
231 if (custom_icon2)
233 tray_icon_free (custom_icon2);
234 custom_icon2 = NULL;
237 tray_status = TS_NONE;
240 static void
241 tray_reset_counts (void)
243 tray_priv_count = 0;
244 tray_pub_count = 0;
245 tray_hilight_count = 0;
246 tray_file_count = 0;
249 static int
250 tray_timeout_cb (TrayIcon icon)
252 if (custom_icon1)
254 if (gtk_status_icon_get_pixbuf (sticon) == custom_icon1)
256 if (custom_icon2)
257 gtk_status_icon_set_from_pixbuf (sticon, custom_icon2);
258 else
259 gtk_status_icon_set_from_pixbuf (sticon, ICON_NORMAL);
261 else
263 gtk_status_icon_set_from_pixbuf (sticon, custom_icon1);
266 else
268 if (gtk_status_icon_get_pixbuf (sticon) == ICON_NORMAL)
269 gtk_status_icon_set_from_pixbuf (sticon, icon);
270 else
271 gtk_status_icon_set_from_pixbuf (sticon, ICON_NORMAL);
273 return 1;
276 static void
277 tray_set_flash (TrayIcon icon)
279 if (!sticon)
280 return;
282 /* already flashing the same icon */
283 if (flash_tag && gtk_status_icon_get_pixbuf (sticon) == icon)
284 return;
286 /* no flashing if window is focused */
287 if (tray_get_window_status () == WS_FOCUSED)
288 return;
290 tray_stop_flash ();
292 gtk_status_icon_set_from_pixbuf (sticon, icon);
293 flash_tag = g_timeout_add (TIMEOUT, (GSourceFunc) tray_timeout_cb, icon);
296 void
297 fe_tray_set_flash (const char *filename1, const char *filename2, int tout)
299 tray_apply_setup ();
300 if (!sticon)
301 return;
303 tray_stop_flash ();
305 if (tout == -1)
306 tout = TIMEOUT;
308 custom_icon1 = tray_icon_from_file (filename1);
309 if (filename2)
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;
317 void
318 fe_tray_set_icon (feicon icon)
320 tray_apply_setup ();
321 if (!sticon)
322 return;
324 tray_stop_flash ();
326 switch (icon)
328 case FE_ICON_NORMAL:
329 break;
330 case FE_ICON_MESSAGE:
331 tray_set_flash (ICON_MSG);
332 break;
333 case FE_ICON_HIGHLIGHT:
334 case FE_ICON_PRIVMSG:
335 tray_set_flash (ICON_HILIGHT);
336 break;
337 case FE_ICON_FILEOFFER:
338 tray_set_flash (ICON_FILE);
342 void
343 fe_tray_set_file (const char *filename)
345 tray_apply_setup ();
346 if (!sticon)
347 return;
349 tray_stop_flash ();
351 if (filename)
353 custom_icon1 = tray_icon_from_file (filename);
354 gtk_status_icon_set_from_pixbuf (sticon, custom_icon1);
355 tray_status = TS_CUSTOM;
359 gboolean
360 tray_toggle_visibility (gboolean force_hide)
362 static int x, y;
363 static GdkScreen *screen;
364 GtkWindow *win;
366 if (!sticon)
367 return FALSE;
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");
374 tray_stop_flash ();
375 tray_reset_counts ();
377 if (!win)
378 return FALSE;
380 #if GTK_CHECK_VERSION(2,20,0)
381 if (force_hide || gtk_widget_get_visible (GTK_WIDGET (win)))
382 #else
383 if (force_hide || GTK_WIDGET_VISIBLE (win))
384 #endif
386 gtk_window_get_position (win, &x, &y);
387 screen = gtk_window_get_screen (win);
388 gtk_widget_hide (GTK_WIDGET (win));
390 else
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);
398 return TRUE;
401 static void
402 tray_menu_restore_cb (GtkWidget *item, gpointer userdata)
404 tray_toggle_visibility (FALSE);
407 static void
408 tray_menu_quit_cb (GtkWidget *item, gpointer userdata)
410 mg_open_quit_dialog (FALSE);
413 /* returns 0-mixed 1-away 2-back */
415 static int
416 tray_find_away_status (void)
418 GSList *list;
419 server *serv;
420 int away = 0;
421 int back = 0;
423 for (list = serv_list; list; list = list->next)
425 serv = list->data;
427 if (serv->is_away || serv->reconnect_away)
428 away++;
429 else
430 back++;
433 if (away && back)
434 return 0;
436 if (away)
437 return 1;
439 return 2;
442 static void
443 tray_foreach_server (GtkWidget *item, char *cmd)
445 GSList *list;
446 server *serv;
448 for (list = serv_list; list; list = list->next)
450 serv = list->data;
451 if (serv->connected)
452 handle_command (serv->server_session, cmd, FALSE);
456 static GtkWidget *
457 tray_make_item (GtkWidget *menu, char *label, void *callback, void *userdata)
459 GtkWidget *item;
461 if (label)
462 item = gtk_menu_item_new_with_mnemonic (label);
463 else
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);
470 return item;
473 static void
474 tray_toggle_cb (GtkCheckMenuItem *item, unsigned int *setting)
476 *setting = item->active;
479 static void
480 blink_item (unsigned int *setting, GtkWidget *menu, char *label)
482 menu_toggle_item (label, menu, tray_toggle_cb, setting, *setting);
485 static void
486 tray_menu_destroy (GtkWidget *menu, gpointer userdata)
488 gtk_widget_destroy (menu);
489 g_object_unref (menu);
492 static void
493 tray_menu_cb (GtkWidget *widget, guint button, guint time, gpointer userdata)
495 GtkWidget *menu;
496 GtkWidget *submenu;
497 GtkWidget *item;
498 int away_status;
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);
508 else
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);
532 g_object_ref (menu);
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);
542 static void
543 tray_init (void)
545 flash_tag = 0;
546 tray_status = TS_NONE;
547 custom_icon1 = NULL;
548 custom_icon2 = NULL;
550 sticon = gtk_status_icon_new_from_pixbuf (ICON_NORMAL);
551 if (!sticon)
552 return;
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);
559 static int
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"));
574 else
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;
586 static int
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);
596 tray_pub_count++;
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"));
600 else
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;
611 static void
612 tray_priv (char *from, char *text)
614 const char *network;
616 if (alert_match_word (from, prefs.irc_no_hilight))
617 return;
619 tray_set_flash (ICON_HILIGHT);
621 network = xchat_get_info (ph, "network");
622 if (!network)
623 network = xchat_get_info (ph, "server");
625 tray_priv_count++;
626 if (tray_priv_count == 1)
627 tray_set_tipf (_("XChat: Private message from: %s (%s)"),
628 from, network);
629 else
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)"),
635 from, network);
638 static int
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;
650 static int
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;
662 static int
663 tray_dcc_cb (char *word[], void *userdata)
665 const char *network;
667 /* if (tray_status == TS_FILEOFFER)
668 return XCHAT_EAT_NONE;*/
670 network = xchat_get_info (ph, "network");
671 if (!network)
672 network = xchat_get_info (ph, "server");
674 if (prefs.input_tray_priv)
676 tray_set_flash (ICON_FILE);
678 tray_file_count++;
679 if (tray_file_count == 1)
680 tray_set_tipf (_("XChat: File offer from: %s (%s)"),
681 word[1], network);
682 else
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)"),
689 word[1], network);
691 return XCHAT_EAT_NONE;
694 static int
695 tray_focus_cb (char *word[], void *userdata)
697 tray_stop_flash ();
698 tray_reset_counts ();
699 return XCHAT_EAT_NONE;
702 static void
703 tray_cleanup (void)
705 tray_stop_flash ();
707 if (sticon)
709 g_object_unref ((GObject *)sticon);
710 sticon = NULL;
714 void
715 tray_apply_setup (void)
717 if (sticon)
719 if (!prefs.gui_tray)
720 tray_cleanup ();
722 else
724 if (prefs.gui_tray)
725 tray_init ();
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 */
734 ph = plugin_handle;
736 *plugin_name = "";
737 *plugin_desc = "";
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);
756 if (prefs.gui_tray)
757 tray_init ();
759 return 1; /* return 1 for success */
763 tray_plugin_deinit (xchat_plugin *plugin_handle)
765 #if defined(USE_LIBNOTIFY)
766 notify_uninit ();
767 #endif
768 return 1;