Save us 35 warnings about hooks_register_hook() return checks.
[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 (folder_has_parent_of_type(update_data->item, F_DRAFT))
159       return FALSE;
160
161 #if defined(NOTIFICATION_LCDPROC) || defined(NOTIFICATION_TRAYICON) || defined(NOTIFICATION_INDICATOR)
162     notification_update_msg_counts(NULL);
163 #else
164     if(notify_config.urgency_hint_new || notify_config.urgency_hint_unread)
165         notification_update_msg_counts(NULL);
166 #endif
167
168   /* Check if the folder types is to be notified about */
169   ftype = update_data->item->folder->klass->type;
170   uistr = update_data->item->folder->klass->uistr;
171   if(!notify_include_folder_type(ftype, uistr))
172     return FALSE;
173
174   if(update_data->update_flags & F_ITEM_UPDATE_MSGCNT) {
175 #ifdef NOTIFICATION_BANNER
176     notification_update_banner();
177 #endif
178 #if defined(NOTIFICATION_POPUP) || defined(NOTIFICATION_COMMAND)
179     notification_new_unnotified_msgs(update_data);
180 #endif
181   }
182   return FALSE;
183 }
184
185 static gboolean my_folder_update_hook(gpointer source, gpointer data)
186 {
187   FolderUpdateData *hookdata;
188
189   g_return_val_if_fail(source != NULL, FALSE);
190   hookdata = source;
191
192 #if defined(NOTIFICATION_LCDPROC) || defined(NOTIFICATION_TRAYICON)
193   if(hookdata->update_flags & FOLDER_REMOVE_FOLDERITEM)
194     notification_update_msg_counts(hookdata->item);
195   else
196     notification_update_msg_counts(NULL);
197 #endif
198
199   return FALSE;
200
201 }
202
203
204 static gboolean my_msginfo_update_hook(gpointer source, gpointer data)
205 {
206   return notification_notified_hash_msginfo_update((MsgInfoUpdate*)source);
207 }
208
209 gint plugin_init(gchar **error)
210 {
211   gchar *rcpath;
212
213   /* Version check */
214   /* No be able to test against new-contacts */
215   if(!check_plugin_version(MAKE_NUMERIC_VERSION(3,8,1,46),
216                            VERSION_NUMERIC, _("Notification"), error))
217     return -1;
218
219   /* Check if threading is enabled */
220   if(!g_thread_supported()) {
221     *error = g_strdup(_("The Notification plugin needs threading support."));
222     return -1;
223   }
224
225   hook_f_item = hooks_register_hook(FOLDER_ITEM_UPDATE_HOOKLIST,
226                                     my_folder_item_update_hook, NULL);
227   if(hook_f_item == (guint) -1) {
228     *error = g_strdup(_("Failed to register folder item update hook in the "
229                         "Notification plugin"));
230     return -1;
231   }
232
233   hook_f = hooks_register_hook(FOLDER_UPDATE_HOOKLIST,
234                                my_folder_update_hook, NULL);
235   if(hook_f == (guint) -1) {
236     *error = g_strdup(_("Failed to register folder update hook in the "
237                         "Notification plugin"));
238     hooks_unregister_hook(FOLDER_ITEM_UPDATE_HOOKLIST, hook_f_item);
239     return -1;
240   }
241
242
243   hook_m_info = hooks_register_hook(MSGINFO_UPDATE_HOOKLIST,
244                                     my_msginfo_update_hook, NULL);
245   if(hook_m_info == (guint) -1) {
246     *error = g_strdup(_("Failed to register msginfo update hook in the "
247                         "Notification plugin"));
248     hooks_unregister_hook(FOLDER_ITEM_UPDATE_HOOKLIST, hook_f_item);
249     hooks_unregister_hook(FOLDER_UPDATE_HOOKLIST, hook_f);
250     return -1;
251   }
252
253   hook_offline = hooks_register_hook(OFFLINE_SWITCH_HOOKLIST,
254                                      my_offline_switch_hook, NULL);
255   if(hook_offline == (guint) -1) {
256     *error = g_strdup(_("Failed to register offline switch hook in the "
257                         "Notification plugin"));
258     hooks_unregister_hook(FOLDER_ITEM_UPDATE_HOOKLIST, hook_f_item);
259     hooks_unregister_hook(FOLDER_UPDATE_HOOKLIST, hook_f);
260     hooks_unregister_hook(MSGINFO_UPDATE_HOOKLIST, hook_m_info);
261     return -1;
262   }
263
264   hook_mw_close = hooks_register_hook(MAIN_WINDOW_CLOSE,
265                                       my_main_window_close_hook, NULL);
266   if(hook_mw_close == (guint) -1) {
267     *error = g_strdup(_("Failed to register main window close hook in the "
268                         "Notification plugin"));
269     hooks_unregister_hook(FOLDER_ITEM_UPDATE_HOOKLIST, hook_f_item);
270     hooks_unregister_hook(FOLDER_UPDATE_HOOKLIST, hook_f);
271     hooks_unregister_hook(MSGINFO_UPDATE_HOOKLIST, hook_m_info);
272     hooks_unregister_hook(OFFLINE_SWITCH_HOOKLIST, hook_offline);
273     return -1;
274   }
275
276   hook_got_iconified = hooks_register_hook(MAIN_WINDOW_GOT_ICONIFIED,
277                                            my_main_window_got_iconified_hook,
278                                            NULL);
279   if(hook_got_iconified == (guint) -1) {
280     *error = g_strdup(_("Failed to register got iconified hook in the "
281                         "Notification plugin"));
282     hooks_unregister_hook(FOLDER_ITEM_UPDATE_HOOKLIST, hook_f_item);
283     hooks_unregister_hook(FOLDER_UPDATE_HOOKLIST, hook_f);
284     hooks_unregister_hook(MSGINFO_UPDATE_HOOKLIST, hook_m_info);
285     hooks_unregister_hook(OFFLINE_SWITCH_HOOKLIST, hook_offline);
286     hooks_unregister_hook(MAIN_WINDOW_CLOSE, hook_mw_close);
287     return -1;
288   }
289
290   hook_account = hooks_register_hook(ACCOUNT_LIST_CHANGED_HOOKLIST,
291                                      my_account_list_changed_hook, NULL);
292   if (hook_account == (guint) -1) {
293     *error = g_strdup(_("Failed to register account list changed hook in the "
294                         "Notification plugin"));
295     hooks_unregister_hook(FOLDER_ITEM_UPDATE_HOOKLIST, hook_f_item);
296     hooks_unregister_hook(FOLDER_UPDATE_HOOKLIST, hook_f);
297     hooks_unregister_hook(MSGINFO_UPDATE_HOOKLIST, hook_m_info);
298     hooks_unregister_hook(OFFLINE_SWITCH_HOOKLIST, hook_offline);
299     hooks_unregister_hook(MAIN_WINDOW_CLOSE, hook_mw_close);
300     hooks_unregister_hook(MAIN_WINDOW_GOT_ICONIFIED, hook_got_iconified);
301     return -1;
302   }
303
304   hook_theme_changed = hooks_register_hook(THEME_CHANGED_HOOKLIST, my_update_theme_hook, NULL);
305   if(hook_theme_changed == (guint) -1) {
306     *error = g_strdup(_("Failed to register theme change hook in the "
307       "Notification plugin"));
308     hooks_unregister_hook(FOLDER_ITEM_UPDATE_HOOKLIST, hook_f_item);
309     hooks_unregister_hook(FOLDER_UPDATE_HOOKLIST, hook_f);
310     hooks_unregister_hook(MSGINFO_UPDATE_HOOKLIST, hook_m_info);
311     hooks_unregister_hook(OFFLINE_SWITCH_HOOKLIST, hook_offline);
312     hooks_unregister_hook(MAIN_WINDOW_CLOSE, hook_mw_close);
313     hooks_unregister_hook(MAIN_WINDOW_GOT_ICONIFIED, hook_got_iconified);
314     hooks_unregister_hook(ACCOUNT_LIST_CHANGED_HOOKLIST, hook_account);
315     return -1;
316   }
317
318   /* Configuration */
319   prefs_set_default(notify_param);
320   rcpath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, COMMON_RC, NULL);
321   prefs_read_config(notify_param, "NotificationPlugin", rcpath, NULL);
322   g_free(rcpath);
323
324   /* Folder specific stuff */
325   notification_foldercheck_read_array();
326
327   notification_notified_hash_startup_init();
328
329   notify_gtk_init();
330
331 #ifdef NOTIFICATION_INDICATOR
332   notification_indicator_setup();
333 #endif
334 #ifdef NOTIFICATION_BANNER
335   notification_update_banner();
336 #endif
337 #ifdef NOTIFICATION_LCDPROC
338   notification_lcdproc_connect();
339 #endif
340 #ifdef NOTIFICATION_TRAYICON
341   if(notify_config.trayicon_enabled &&
342                  notify_config.trayicon_hide_at_startup && claws_is_starting()) {
343     MainWindow *mainwin = mainwindow_get_mainwindow();
344
345                 g_idle_add(trayicon_startup_idle,NULL);
346     if(mainwin && gtk_widget_get_visible(GTK_WIDGET(mainwin->window)))
347       main_window_hide(mainwin);
348     main_set_show_at_startup(FALSE);
349   }
350 #endif
351   my_account_list_changed_hook(NULL,NULL);
352
353   if(notify_config.urgency_hint_new || notify_config.urgency_hint_unread)
354         notification_update_msg_counts(NULL);
355
356 #ifdef NOTIFICATION_HOTKEYS
357   notification_hotkeys_update_bindings();
358 #endif
359
360   debug_print("Notification plugin loaded\n");
361
362   return 0;
363 }
364
365 gboolean plugin_done(void)
366 {
367   hooks_unregister_hook(FOLDER_ITEM_UPDATE_HOOKLIST, hook_f_item);
368   hooks_unregister_hook(FOLDER_UPDATE_HOOKLIST, hook_f);
369   hooks_unregister_hook(MSGINFO_UPDATE_HOOKLIST, hook_m_info);
370   hooks_unregister_hook(OFFLINE_SWITCH_HOOKLIST, hook_offline);
371   hooks_unregister_hook(MAIN_WINDOW_CLOSE, hook_mw_close);
372   hooks_unregister_hook(MAIN_WINDOW_GOT_ICONIFIED, hook_got_iconified);
373   hooks_unregister_hook(ACCOUNT_LIST_CHANGED_HOOKLIST, hook_account);
374   hooks_unregister_hook(THEME_CHANGED_HOOKLIST, hook_theme_changed);
375
376   notify_save_config();
377
378   notify_gtk_done();
379
380   /* foldercheck cleanup */
381   notification_foldercheck_write_array();
382   notification_free_folder_specific_array();
383
384 #ifdef NOTIFICATION_BANNER
385   notification_collected_msgs_free(banner_collected_msgs);
386   banner_collected_msgs = NULL;
387   notification_banner_destroy();
388 #endif
389 #ifdef NOTIFICATION_LCDPROC
390   notification_lcdproc_disconnect();
391 #endif
392 #ifdef NOTIFICATION_TRAYICON
393   notification_trayicon_destroy();
394 #endif
395 #ifdef NOTIFICATION_INDICATOR
396   notification_indicator_destroy();
397 #endif
398   notification_core_free();
399
400 #ifdef HAVE_LIBNOTIFY
401   if(notify_is_initted())
402     notify_uninit();
403 #endif
404
405 #ifdef NOTIFICATION_HOTKEYS
406   notification_hotkeys_unbind_all();
407 #endif
408
409   notification_pixbuf_free_all();
410
411   debug_print("Notification plugin unloaded\n");
412
413   /* Returning FALSE here means that g_module_close() will not be called on the plugin.
414    * This is necessary, as needed libraries are not designed to be unloaded. */
415   return FALSE;
416 }
417
418 const gchar *plugin_name(void)
419 {
420   return _("Notification");
421 }
422
423 const gchar *plugin_desc(void)
424 {
425   return _("This plugin provides various ways "
426     "to notify the user of new and unread email.\n"
427     "The plugin is extensively configurable in the "
428     "plugins section of the preferences dialog.\n\n"
429     "Feedback to <berndth@gmx.de> is welcome.");
430 }
431
432 const gchar *plugin_type(void)
433 {
434   return "GTK2";
435 }
436
437 const gchar *plugin_licence(void)
438 {
439   return "GPL3+";
440 }
441
442 const gchar *plugin_version(void)
443 {
444   return VERSION;
445 }
446
447 struct PluginFeature *plugin_provides(void)
448 {
449   static struct PluginFeature features[] =
450     { {PLUGIN_NOTIFIER, N_("Various tools")},
451       {PLUGIN_NOTHING, NULL}};
452   return features;
453 }
454
455 #ifdef NOTIFICATION_BANNER
456 void notification_update_banner(void)
457 {
458   notification_collected_msgs_free(banner_collected_msgs);
459   banner_collected_msgs = NULL;
460
461   if(notify_config.banner_show != NOTIFY_BANNER_SHOW_NEVER) {
462     guint id;
463     GSList *folder_list = NULL;
464
465     if(notify_config.banner_folder_specific) {
466       id = notification_register_folder_specific_list
467                                 (BANNER_SPECIFIC_FOLDER_ID_STR);
468       folder_list = notification_foldercheck_get_list(id);
469     }
470
471     if(!(notify_config.banner_folder_specific && (folder_list == NULL)))
472       banner_collected_msgs =
473                                 notification_collect_msgs(notify_config.banner_include_unread,
474                                                                                                                                         notify_config.banner_folder_specific ?
475                                                                                                                                         folder_list : NULL, notify_config.banner_max_msgs);
476   }
477
478   notification_banner_show(banner_collected_msgs);
479 }
480 #endif
481
482 #ifdef NOTIFICATION_TRAYICON
483 static gboolean trayicon_startup_idle(gpointer data)
484 {
485         /* if the trayicon is not available,
486                  simulate click on it to show mainwindow */
487         if(!notification_trayicon_is_available())
488                 notification_trayicon_on_activate(NULL,data);
489         return FALSE;
490 }
491 #endif