1b7d14a832fad2d92f3e41fd44f32ff26ec3fd7e
[claws.git] / src / plugins / notification / notification_plugin.c
1 /* Notification plugin for Claws-Mail
2  * Copyright (C) 2005-2007 Holger Berndt
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 3 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program. If not, see <http://www.gnu.org/licenses/>.
16  */
17
18 #ifdef HAVE_CONFIG_H
19 #  include "config.h"
20 #  include "claws-features.h"
21 #endif
22
23 #include <glib.h>
24 #include <glib/gi18n.h>
25
26 #include "common/claws.h"
27 #include "common/version.h"
28 #include "common/utils.h"
29 #include "common/defs.h"
30 #include "folder.h"
31 #include "common/hooks.h"
32 #include "procmsg.h"
33 #include "mainwindow.h"
34 #include "main.h"
35 #include "gtk/gtkutils.h"
36
37 #include "notification_plugin.h"
38 #include "notification_core.h"
39 #include "notification_prefs.h"
40 #include "notification_banner.h"
41 #include "notification_lcdproc.h"
42 #include "notification_trayicon.h"
43 #include "notification_indicator.h"
44 #include "notification_hotkeys.h"
45 #include "notification_foldercheck.h"
46 #include "notification_pixbuf.h"
47 #include "plugin.h"
48
49 #if HAVE_LIBNOTIFY
50 #  include <libnotify/notify.h>
51 #endif
52
53
54 static gboolean my_folder_item_update_hook(gpointer, gpointer);
55 static gboolean my_folder_update_hook(gpointer, gpointer);
56 static gboolean my_msginfo_update_hook(gpointer, gpointer);
57 static gboolean my_offline_switch_hook(gpointer, gpointer);
58 static gboolean my_main_window_close_hook(gpointer, gpointer);
59 static gboolean my_main_window_got_iconified_hook(gpointer, gpointer);
60 static gboolean my_account_list_changed_hook(gpointer, gpointer);
61 static gboolean my_update_theme_hook(gpointer, gpointer);
62
63 #ifdef NOTIFICATION_TRAYICON
64 static gboolean trayicon_startup_idle(gpointer);
65 #endif
66
67 static guint hook_f_item;
68 static guint hook_f;
69 static guint hook_m_info;
70 static guint hook_offline;
71 static guint hook_mw_close;
72 static guint hook_got_iconified;
73 static guint hook_account;
74 static guint hook_theme_changed;
75
76 #ifdef NOTIFICATION_BANNER
77 static GSList* banner_collected_msgs;
78 #endif
79
80 void notification_update_urgency_hint(void)
81 {
82   MainWindow *mainwin;
83   mainwin = mainwindow_get_mainwindow();
84   if(mainwin) {
85     NotificationMsgCount count;
86     gboolean active;
87     active = FALSE;
88     if(notify_config.urgency_hint_new || notify_config.urgency_hint_unread) {
89       notification_core_get_msg_count(NULL, &count);
90       if(notify_config.urgency_hint_new)
91         active = (active || (count.new_msgs > 0));
92       if(notify_config.urgency_hint_unread)
93         active = (active || (count.unread_msgs > 0));
94     }
95     gtk_window_set_urgency_hint(GTK_WINDOW(mainwin->window), active);
96   }
97 }
98
99 static gboolean my_account_list_changed_hook(gpointer source,
100                                              gpointer data)
101 {
102   gboolean retVal = FALSE;
103
104 #ifdef NOTIFICATION_TRAYICON
105   notification_update_msg_counts(NULL);
106   retVal = notification_trayicon_account_list_changed(source, data);
107
108 #endif
109   return retVal;
110 }
111
112 static gboolean my_update_theme_hook(gpointer source, gpointer data)
113 {
114   notification_pixbuf_free_all();
115 #ifdef NOTIFICATION_TRAYICON
116   notification_update_trayicon();
117 #endif
118   return FALSE;
119 }
120
121 static gboolean my_main_window_got_iconified_hook(gpointer source,
122                                                   gpointer data)
123 {
124   gboolean retVal = FALSE;
125 #ifdef NOTIFICATION_TRAYICON
126   notification_update_msg_counts(NULL);
127   retVal = notification_trayicon_main_window_got_iconified(source, data);
128 #endif
129   return retVal;
130 }
131
132 static gboolean my_main_window_close_hook(gpointer source, gpointer data)
133 {
134   gboolean retVal = FALSE;
135 #ifdef NOTIFICATION_TRAYICON
136   notification_update_msg_counts(NULL);
137   retVal = notification_trayicon_main_window_close(source, data);
138 #endif
139   return retVal;
140 }
141
142 static gboolean my_offline_switch_hook(gpointer source, gpointer data)
143 {
144 #ifdef NOTIFICATION_TRAYICON
145   notification_update_msg_counts(NULL);
146 #endif
147   return FALSE;
148 }
149
150 static gboolean my_folder_item_update_hook(gpointer source, gpointer data)
151 {
152   FolderItemUpdateData *update_data = source;
153   FolderType ftype;
154   gchar *uistr;
155
156   g_return_val_if_fail(source != NULL, FALSE);
157
158 #if defined(NOTIFICATION_LCDPROC) || defined(NOTIFICATION_TRAYICON) || defined(NOTIFICATION_INDICATOR)
159     notification_update_msg_counts(NULL);
160 #else
161     if(notify_config.urgency_hint)
162         notification_update_msg_counts(NULL);
163 #endif
164
165   /* Check if the folder types is to be notified about */
166   ftype = update_data->item->folder->klass->type;
167   uistr = update_data->item->folder->klass->uistr;
168   if(!notify_include_folder_type(ftype, uistr))
169     return FALSE;
170
171   if(update_data->update_flags & F_ITEM_UPDATE_MSGCNT) {
172 #ifdef NOTIFICATION_BANNER
173     notification_update_banner();
174 #endif
175 #if defined(NOTIFICATION_POPUP) || defined(NOTIFICATION_COMMAND)
176     notification_new_unnotified_msgs(update_data);
177 #endif
178   }
179   return FALSE;
180 }
181
182 static gboolean my_folder_update_hook(gpointer source, gpointer data)
183 {
184   FolderUpdateData *hookdata;
185
186   g_return_val_if_fail(source != NULL, FALSE);
187   hookdata = source;
188
189 #if defined(NOTIFICATION_LCDPROC) || defined(NOTIFICATION_TRAYICON)
190   if(hookdata->update_flags & FOLDER_REMOVE_FOLDERITEM)
191     notification_update_msg_counts(hookdata->item);
192   else
193     notification_update_msg_counts(NULL);
194 #endif
195
196   return FALSE;
197
198 }
199
200
201 static gboolean my_msginfo_update_hook(gpointer source, gpointer data)
202 {
203   return notification_notified_hash_msginfo_update((MsgInfoUpdate*)source);
204 }
205
206 gint plugin_init(gchar **error)
207 {
208   gchar *rcpath;
209
210   /* Version check */
211   /* No be able to test against new-contacts */
212   if(!check_plugin_version(MAKE_NUMERIC_VERSION(3,8,1,46),
213                            VERSION_NUMERIC, _("Notification"), error))
214     return -1;
215
216   /* Check if threading is enabled */
217   if(!g_thread_supported()) {
218     *error = g_strdup(_("The Notification plugin needs threading support."));
219     return -1;
220   }
221
222   hook_f_item = hooks_register_hook(FOLDER_ITEM_UPDATE_HOOKLIST,
223                                     my_folder_item_update_hook, NULL);
224   if(hook_f_item == (guint) -1) {
225     *error = g_strdup(_("Failed to register folder item update hook in the "
226                         "Notification plugin"));
227     return -1;
228   }
229
230   hook_f = hooks_register_hook(FOLDER_UPDATE_HOOKLIST,
231                                my_folder_update_hook, NULL);
232   if(hook_f == (guint) -1) {
233     *error = g_strdup(_("Failed to register folder update hook in the "
234                         "Notification plugin"));
235     hooks_unregister_hook(FOLDER_ITEM_UPDATE_HOOKLIST, hook_f_item);
236     return -1;
237   }
238
239
240   hook_m_info = hooks_register_hook(MSGINFO_UPDATE_HOOKLIST,
241                                     my_msginfo_update_hook, NULL);
242   if(hook_m_info == (guint) -1) {
243     *error = g_strdup(_("Failed to register msginfo update hook in the "
244                         "Notification plugin"));
245     hooks_unregister_hook(FOLDER_ITEM_UPDATE_HOOKLIST, hook_f_item);
246     hooks_unregister_hook(FOLDER_UPDATE_HOOKLIST, hook_f);
247     return -1;
248   }
249
250   hook_offline = hooks_register_hook(OFFLINE_SWITCH_HOOKLIST,
251                                      my_offline_switch_hook, NULL);
252   if(hook_offline == (guint) -1) {
253     *error = g_strdup(_("Failed to register offline switch hook in the "
254                         "Notification plugin"));
255     hooks_unregister_hook(FOLDER_ITEM_UPDATE_HOOKLIST, hook_f_item);
256     hooks_unregister_hook(FOLDER_UPDATE_HOOKLIST, hook_f);
257     hooks_unregister_hook(MSGINFO_UPDATE_HOOKLIST, hook_m_info);
258     return -1;
259   }
260
261   hook_mw_close = hooks_register_hook(MAIN_WINDOW_CLOSE,
262                                       my_main_window_close_hook, NULL);
263   if(hook_mw_close == (guint) -1) {
264     *error = g_strdup(_("Failed to register main window close hook in the "
265                         "Notification plugin"));
266     hooks_unregister_hook(FOLDER_ITEM_UPDATE_HOOKLIST, hook_f_item);
267     hooks_unregister_hook(FOLDER_UPDATE_HOOKLIST, hook_f);
268     hooks_unregister_hook(MSGINFO_UPDATE_HOOKLIST, hook_m_info);
269     hooks_unregister_hook(OFFLINE_SWITCH_HOOKLIST, hook_offline);
270     return -1;
271   }
272
273   hook_got_iconified = hooks_register_hook(MAIN_WINDOW_GOT_ICONIFIED,
274                                            my_main_window_got_iconified_hook,
275                                            NULL);
276   if(hook_got_iconified == (guint) -1) {
277     *error = g_strdup(_("Failed to register got iconified hook in the "
278                         "Notification plugin"));
279     hooks_unregister_hook(FOLDER_ITEM_UPDATE_HOOKLIST, hook_f_item);
280     hooks_unregister_hook(FOLDER_UPDATE_HOOKLIST, hook_f);
281     hooks_unregister_hook(MSGINFO_UPDATE_HOOKLIST, hook_m_info);
282     hooks_unregister_hook(OFFLINE_SWITCH_HOOKLIST, hook_offline);
283     hooks_unregister_hook(MAIN_WINDOW_CLOSE, hook_mw_close);
284     return -1;
285   }
286
287   hook_account = hooks_register_hook(ACCOUNT_LIST_CHANGED_HOOKLIST,
288                                      my_account_list_changed_hook, NULL);
289   if (hook_account == (guint) -1) {
290     *error = g_strdup(_("Failed to register account list changed hook in the "
291                         "Notification plugin"));
292     hooks_unregister_hook(FOLDER_ITEM_UPDATE_HOOKLIST, hook_f_item);
293     hooks_unregister_hook(FOLDER_UPDATE_HOOKLIST, hook_f);
294     hooks_unregister_hook(MSGINFO_UPDATE_HOOKLIST, hook_m_info);
295     hooks_unregister_hook(OFFLINE_SWITCH_HOOKLIST, hook_offline);
296     hooks_unregister_hook(MAIN_WINDOW_CLOSE, hook_mw_close);
297     hooks_unregister_hook(MAIN_WINDOW_GOT_ICONIFIED, hook_got_iconified);
298     return -1;
299   }
300
301   hook_theme_changed = hooks_register_hook(THEME_CHANGED_HOOKLIST, my_update_theme_hook, NULL);
302   if(hook_theme_changed == (guint)-1) {
303     *error = g_strdup(_("Failed to register theme change hook int the "
304       "Notification plugin"));
305     hooks_unregister_hook(FOLDER_ITEM_UPDATE_HOOKLIST, hook_f_item);
306     hooks_unregister_hook(FOLDER_UPDATE_HOOKLIST, hook_f);
307     hooks_unregister_hook(MSGINFO_UPDATE_HOOKLIST, hook_m_info);
308     hooks_unregister_hook(OFFLINE_SWITCH_HOOKLIST, hook_offline);
309     hooks_unregister_hook(MAIN_WINDOW_CLOSE, hook_mw_close);
310     hooks_unregister_hook(MAIN_WINDOW_GOT_ICONIFIED, hook_got_iconified);
311     hooks_unregister_hook(ACCOUNT_LIST_CHANGED_HOOKLIST, hook_account);
312     return -1;
313   }
314
315   /* Configuration */
316   prefs_set_default(notify_param);
317   rcpath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, COMMON_RC, NULL);
318   prefs_read_config(notify_param, "NotificationPlugin", rcpath, NULL);
319   g_free(rcpath);
320
321   /* Folder specific stuff */
322   notification_foldercheck_read_array();
323
324   notification_notified_hash_startup_init();
325
326   notify_gtk_init();
327
328 #ifdef NOTIFICATION_BANNER
329   notification_update_banner();
330 #endif
331 #ifdef NOTIFICATION_LCDPROC
332   notification_lcdproc_connect();
333 #endif
334 #ifdef NOTIFICATION_TRAYICON
335   if(notify_config.trayicon_enabled &&
336                  notify_config.trayicon_hide_at_startup && claws_is_starting()) {
337     MainWindow *mainwin = mainwindow_get_mainwindow();
338
339                 g_idle_add(trayicon_startup_idle,NULL);
340     if(mainwin && gtk_widget_get_visible(GTK_WIDGET(mainwin->window)))
341       main_window_hide(mainwin);
342     main_set_show_at_startup(FALSE);
343   }
344 #endif
345   my_account_list_changed_hook(NULL,NULL);
346
347   if(notify_config.urgency_hint_new || notify_config.urgency_hint_unread)
348         notification_update_msg_counts(NULL);
349
350 #ifdef NOTIFICATION_HOTKEYS
351   notification_hotkeys_update_bindings();
352 #endif
353
354   debug_print("Notification plugin loaded\n");
355
356   return 0;
357 }
358
359 gboolean plugin_done(void)
360 {
361   hooks_unregister_hook(FOLDER_ITEM_UPDATE_HOOKLIST, hook_f_item);
362   hooks_unregister_hook(FOLDER_UPDATE_HOOKLIST, hook_f);
363   hooks_unregister_hook(MSGINFO_UPDATE_HOOKLIST, hook_m_info);
364   hooks_unregister_hook(OFFLINE_SWITCH_HOOKLIST, hook_offline);
365   hooks_unregister_hook(MAIN_WINDOW_CLOSE, hook_mw_close);
366   hooks_unregister_hook(MAIN_WINDOW_GOT_ICONIFIED, hook_got_iconified);
367   hooks_unregister_hook(ACCOUNT_LIST_CHANGED_HOOKLIST, hook_account);
368   hooks_unregister_hook(THEME_CHANGED_HOOKLIST, hook_theme_changed);
369
370   notify_save_config();
371
372   notify_gtk_done();
373
374   /* foldercheck cleanup */
375   notification_foldercheck_write_array();
376   notification_free_folder_specific_array();
377
378 #ifdef NOTIFICATION_BANNER
379   notification_collected_msgs_free(banner_collected_msgs);
380   banner_collected_msgs = NULL;
381   notification_banner_destroy();
382 #endif
383 #ifdef NOTIFICATION_LCDPROC
384   notification_lcdproc_disconnect();
385 #endif
386 #ifdef NOTIFICATION_TRAYICON
387   notification_trayicon_destroy();
388 #endif
389 #ifdef NOTIFICATION_INDICATOR
390   notification_indicator_destroy();
391 #endif
392   notification_core_free();
393
394 #ifdef HAVE_LIBNOTIFY
395   if(notify_is_initted())
396     notify_uninit();
397 #endif
398
399 #ifdef NOTIFICATION_HOTKEYS
400   notification_hotkeys_unbind_all();
401 #endif
402
403   notification_pixbuf_free_all();
404
405   debug_print("Notification plugin unloaded\n");
406
407   /* Returning FALSE here means that g_module_close() will not be called on the plugin.
408    * This is necessary, as needed libraries are not designed to be unloaded. */
409   return FALSE;
410 }
411
412 const gchar *plugin_name(void)
413 {
414   return _("Notification");
415 }
416
417 const gchar *plugin_desc(void)
418 {
419   return _("This plugin provides various ways "
420     "to notify the user of new and unread email.\n"
421     "The plugin is extensively configurable in the "
422     "plugins section of the preferences dialog.\n\n"
423     "Feedback to <berndth@gmx.de> is welcome.");
424 }
425
426 const gchar *plugin_type(void)
427 {
428   return "GTK2";
429 }
430
431 const gchar *plugin_licence(void)
432 {
433   return "GPL3+";
434 }
435
436 const gchar *plugin_version(void)
437 {
438   return VERSION;
439 }
440
441 struct PluginFeature *plugin_provides(void)
442 {
443   static struct PluginFeature features[] =
444     { {PLUGIN_NOTIFIER, N_("Various tools")},
445       {PLUGIN_NOTHING, NULL}};
446   return features;
447 }
448
449 #ifdef NOTIFICATION_BANNER
450 void notification_update_banner(void)
451 {
452   notification_collected_msgs_free(banner_collected_msgs);
453   banner_collected_msgs = NULL;
454
455   if(notify_config.banner_show != NOTIFY_BANNER_SHOW_NEVER) {
456     guint id;
457     GSList *folder_list = NULL;
458
459     if(notify_config.banner_folder_specific) {
460       id = notification_register_folder_specific_list
461                                 (BANNER_SPECIFIC_FOLDER_ID_STR);
462       folder_list = notification_foldercheck_get_list(id);
463     }
464
465     if(!(notify_config.banner_folder_specific && (folder_list == NULL)))
466       banner_collected_msgs =
467                                 notification_collect_msgs(notify_config.banner_include_unread,
468                                                                                                                                         notify_config.banner_folder_specific ?
469                                                                                                                                         folder_list : NULL, notify_config.banner_max_msgs);
470   }
471
472   notification_banner_show(banner_collected_msgs);
473 }
474 #endif
475
476 #ifdef NOTIFICATION_TRAYICON
477 static gboolean trayicon_startup_idle(gpointer data)
478 {
479         /* if the trayicon is not available,
480                  simulate click on it to show mainwindow */
481         if(!notification_trayicon_is_available())
482                 notification_trayicon_on_activate(NULL,data);
483         return FALSE;
484 }
485 #endif