2007-06-25 Armin Burgmeier <armin@openismus.com>
[modest] / src / widgets / modest-window-mgr.c
1 /* Copyright (c) 2006,2007 Nokia Corporation
2  * All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are
6  * met:
7  *
8  * * Redistributions of source code must retain the above copyright
9  *   notice, this list of conditions and the following disclaimer.
10  * * Redistributions in binary form must reproduce the above copyright
11  *   notice, this list of conditions and the following disclaimer in the
12  *   documentation and/or other materials provided with the distribution.
13  * * Neither the name of the Nokia Corporation nor the names of its
14  *   contributors may be used to endorse or promote products derived from
15  *   this software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
18  * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
20  * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
21  * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  */
29
30 #include <string.h>
31 #include "modest-window-mgr.h"
32 #include "modest-runtime.h"
33 #include "modest-tny-folder.h"
34 #include "modest-ui-actions.h"
35 #include "modest-platform.h"
36 #include "widgets/modest-main-window.h"
37 #include "widgets/modest-msg-edit-window.h"
38 #include "widgets/modest-msg-view-window.h"
39 /* include other impl specific header files */
40
41 /* 'private'/'protected' functions */
42 static void modest_window_mgr_class_init (ModestWindowMgrClass *klass);
43 static void modest_window_mgr_init       (ModestWindowMgr *obj);
44 static void modest_window_mgr_finalize   (GObject *obj);
45
46 static void on_window_destroy            (ModestWindow *window, 
47                                           ModestWindowMgr *self);
48
49 /* list my signals  */
50 enum {
51         /* MY_SIGNAL_1, */
52         /* MY_SIGNAL_2, */
53         LAST_SIGNAL
54 };
55
56 typedef struct _ModestWindowMgrPrivate ModestWindowMgrPrivate;
57 struct _ModestWindowMgrPrivate {
58         GList        *window_list;
59         ModestWindow *main_window;
60         gboolean     fullscreen_mode;
61         gboolean     show_toolbars;
62         gboolean     show_toolbars_fullscreen;
63         
64         GSList       *windows_that_prevent_hibernation;
65         GHashTable   *destroy_handlers;
66 };
67 #define MODEST_WINDOW_MGR_GET_PRIVATE(o)      (G_TYPE_INSTANCE_GET_PRIVATE((o), \
68                                                MODEST_TYPE_WINDOW_MGR, \
69                                                ModestWindowMgrPrivate))
70 /* globals */
71 static GObjectClass *parent_class = NULL;
72
73 /* uncomment the following if you have defined any signals */
74 /* static guint signals[LAST_SIGNAL] = {0}; */
75
76 GType
77 modest_window_mgr_get_type (void)
78 {
79         static GType my_type = 0;
80         if (!my_type) {
81                 static const GTypeInfo my_info = {
82                         sizeof(ModestWindowMgrClass),
83                         NULL,           /* base init */
84                         NULL,           /* base finalize */
85                         (GClassInitFunc) modest_window_mgr_class_init,
86                         NULL,           /* class finalize */
87                         NULL,           /* class data */
88                         sizeof(ModestWindowMgr),
89                         1,              /* n_preallocs */
90                         (GInstanceInitFunc) modest_window_mgr_init,
91                         NULL
92                 };
93                 my_type = g_type_register_static (G_TYPE_OBJECT,
94                                                   "ModestWindowMgr",
95                                                   &my_info, 0);
96         }
97         return my_type;
98 }
99
100 static void
101 modest_window_mgr_class_init (ModestWindowMgrClass *klass)
102 {
103         GObjectClass *gobject_class;
104         gobject_class = (GObjectClass*) klass;
105
106         parent_class            = g_type_class_peek_parent (klass);
107         gobject_class->finalize = modest_window_mgr_finalize;
108
109         g_type_class_add_private (gobject_class, sizeof(ModestWindowMgrPrivate));
110 }
111
112 static void
113 modest_window_mgr_init (ModestWindowMgr *obj)
114 {
115         ModestWindowMgrPrivate *priv;
116
117         priv = MODEST_WINDOW_MGR_GET_PRIVATE(obj);
118         priv->window_list = NULL;
119         priv->main_window = NULL;
120         priv->fullscreen_mode = FALSE;
121
122         /* Could not initialize it from gconf, singletons are not
123            ready yet */
124         priv->show_toolbars = FALSE;
125         priv->show_toolbars_fullscreen = FALSE;
126         priv->destroy_handlers = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_free);
127 }
128
129 static void
130 modest_window_mgr_finalize (GObject *obj)
131 {
132         ModestWindowMgrPrivate *priv = MODEST_WINDOW_MGR_GET_PRIVATE(obj);
133
134         if (priv->window_list) {
135                 GList *iter = priv->window_list;
136                 /* unregister pending windows */
137                 while (iter) {
138                         modest_window_mgr_unregister_window (MODEST_WINDOW_MGR (obj), 
139                                                              MODEST_WINDOW (iter->data));
140                         iter = g_list_next (iter);
141                 }
142                 g_list_free (priv->window_list);
143                 priv->window_list = NULL;
144         }
145
146         /* Free the hash table with the handlers */
147         if (priv->destroy_handlers) {
148                 g_hash_table_destroy (priv->destroy_handlers);
149                 priv->destroy_handlers = NULL;
150         }
151
152         /* Do not unref priv->main_window because it does not hold a
153            new reference */
154
155         G_OBJECT_CLASS(parent_class)->finalize (obj);
156 }
157
158 ModestWindowMgr*
159 modest_window_mgr_new (void)
160 {
161         return MODEST_WINDOW_MGR(g_object_new(MODEST_TYPE_WINDOW_MGR, NULL));
162 }
163
164 void 
165 modest_window_mgr_register_window (ModestWindowMgr *self, 
166                                    ModestWindow *window)
167 {
168         static gboolean first_time = TRUE;
169         GList *win;
170         gboolean show;
171         ModestWindowMgrPrivate *priv;
172         gint *handler_id;
173
174         g_return_if_fail (MODEST_IS_WINDOW_MGR (self));
175         g_return_if_fail (MODEST_IS_WINDOW (window));
176
177         priv = MODEST_WINDOW_MGR_GET_PRIVATE (self);
178
179         win = g_list_find (priv->window_list, window);
180         if (win) {
181                 g_warning ("Trying to register an already registered window");
182                 return;
183         }
184
185         /* Check that it's not a second main window */
186         if (MODEST_IS_MAIN_WINDOW (window)) {
187                 if (priv->main_window) {
188                         g_warning ("Trying to register a second main window");
189                         return;
190                 } else {
191                         priv->main_window = window;
192                 }
193         }
194
195         /* Add to list. Keep a reference to the window */
196         g_object_ref (window);
197         priv->window_list = g_list_prepend (priv->window_list, window);
198
199         /* Listen to object destruction */
200         handler_id = g_malloc0 (sizeof (gint));
201         *handler_id = g_signal_connect (window, "destroy", G_CALLBACK (on_window_destroy), self);
202         g_hash_table_insert (priv->destroy_handlers, window, handler_id);
203
204         /* Put into fullscreen if needed */
205         if (priv->fullscreen_mode)
206                 gtk_window_fullscreen (GTK_WINDOW (window));
207
208         /* Fill caches */
209         if (first_time) {
210                 ModestConf *conf = modest_runtime_get_conf ();
211                 priv->show_toolbars = 
212                         modest_conf_get_bool (conf, MODEST_CONF_SHOW_TOOLBAR, NULL);
213                 priv->show_toolbars_fullscreen = 
214                         modest_conf_get_bool (conf, MODEST_CONF_SHOW_TOOLBAR_FULLSCREEN, NULL);
215                 first_time = FALSE;
216         }
217
218         /* Show/hide toolbar */
219         if (priv->fullscreen_mode)
220                 show = priv->show_toolbars_fullscreen;
221         else
222                 show = priv->show_toolbars;
223         modest_window_show_toolbar (window, show);
224 }
225
226 static void
227 on_window_destroy (ModestWindow *window, ModestWindowMgr *self)
228 {
229         /* Specific stuff first */
230         if (MODEST_IS_MAIN_WINDOW (window)) {
231                 ModestWindowMgrPrivate *priv;
232                 priv = MODEST_WINDOW_MGR_GET_PRIVATE (self);
233
234                 /* If more than one window already opened */
235                 if (g_list_length (priv->window_list) > 1) {
236
237                         /* If the user wants to close all the windows */
238                         if (modest_main_window_close_all (MODEST_MAIN_WINDOW (window))) {
239                                 GList *iter = priv->window_list;
240                                 do {
241                                         if (iter->data != window) {
242                                                 GList *tmp = iter->next;
243                                                 on_window_destroy (MODEST_WINDOW (iter->data),
244                                                                    self);
245                                                 iter = tmp;
246                                         } else {
247                                                 iter = g_list_next (iter);
248                                         }
249                                 } while (iter);
250                         }
251                 }
252         } else {
253                 if (MODEST_IS_MSG_EDIT_WINDOW (window))
254                         /* Save currently edited message to Drafts */
255                         if (modest_msg_edit_window_is_modified (MODEST_MSG_EDIT_WINDOW (window))) {
256                                 gint response = 
257                                         modest_platform_run_confirmation_dialog (GTK_WINDOW (self), 
258                                                                                  _("mcen_nc_no_email_message_modified_save_changes"));
259                                 if (response != GTK_RESPONSE_CANCEL) {
260                                         modest_ui_actions_on_save_to_drafts (NULL, MODEST_MSG_EDIT_WINDOW (window));
261                                 }
262                         }
263         }
264
265         /* Unregister window */
266         modest_window_mgr_unregister_window (self, window);
267 }
268
269 void 
270 modest_window_mgr_unregister_window (ModestWindowMgr *self, 
271                                      ModestWindow *window)
272 {
273         GList *win;
274         ModestWindowMgrPrivate *priv;
275         gint *handler_id;
276
277         g_return_if_fail (MODEST_IS_WINDOW_MGR (self));
278         g_return_if_fail (MODEST_IS_WINDOW (window));
279
280         priv = MODEST_WINDOW_MGR_GET_PRIVATE (self);
281
282         win = g_list_find (priv->window_list, window);
283         if (!win) {
284                 g_warning ("Trying to unregister a window that has not being registered yet");
285                 return;
286         }
287
288         /* If it's the main window unset it */
289         if (priv->main_window == window)
290                 priv->main_window = NULL;
291
292         /* Save state */
293         modest_window_save_state (window);
294
295         /* Remove from list */
296         priv->window_list = g_list_remove_link (priv->window_list, win);
297
298         /* Remove the reference to the window. We need to block the
299            destroy event handler to avoid recursive calls */
300         handler_id = g_hash_table_lookup (priv->destroy_handlers, window);
301         g_signal_handler_block (window, *handler_id);
302         gtk_widget_destroy (win->data);
303         if (G_IS_OBJECT (window)) {
304                 g_warning ("This should not happen the window was not completely destroyed");
305                 g_signal_handler_unblock (window, *handler_id);
306         }
307         g_hash_table_remove (priv->destroy_handlers, window);
308
309         /* If there are no more windows registered then exit program */
310         if (priv->window_list == NULL) {
311                 ModestConf *conf = modest_runtime_get_conf ();
312
313                 /* Save show toolbar status */
314                 modest_conf_set_bool (conf, MODEST_CONF_SHOW_TOOLBAR_FULLSCREEN, 
315                                       priv->show_toolbars_fullscreen, NULL);
316                 modest_conf_set_bool (conf, MODEST_CONF_SHOW_TOOLBAR, 
317                                       priv->show_toolbars, NULL);
318
319                 /* Quit main loop */
320                 gtk_main_quit ();
321         }
322 }
323
324 static gint
325 compare_msguids (ModestWindow *win,
326                  const gchar *uid)
327 {
328         const gchar *msg_uid;
329
330         if (!MODEST_IS_MSG_VIEW_WINDOW (win))
331                 return 1;
332
333         /* Get message uid from msg window */
334         msg_uid = modest_msg_view_window_get_message_uid (MODEST_MSG_VIEW_WINDOW (win));
335
336         if (msg_uid && uid &&!strcmp (msg_uid, uid))
337                 return 0;
338         else
339                 return 1;
340 }
341
342 ModestWindow*  
343 modest_window_mgr_find_window_by_header (ModestWindowMgr *self, 
344                                          TnyHeader *header)
345 {
346         ModestWindowMgrPrivate *priv;
347         GList *win = NULL;
348         gchar *msg_uid;
349
350         g_return_val_if_fail (MODEST_IS_WINDOW_MGR (self), NULL);
351         g_return_val_if_fail (TNY_IS_HEADER (header), NULL);
352
353         priv = MODEST_WINDOW_MGR_GET_PRIVATE (self);
354         msg_uid = modest_tny_folder_get_header_unique_id (header);
355
356         /* Look for the window */
357         if (priv->window_list)
358                 win = g_list_find_custom (priv->window_list, 
359                                           msg_uid, 
360                                           (GCompareFunc) compare_msguids);
361         /* Free */
362         g_free (msg_uid);
363
364         /* Return the window */
365         if (win)
366                 return win->data;
367         else 
368                 return NULL;
369 }
370
371 void
372 modest_window_mgr_set_fullscreen_mode (ModestWindowMgr *self,
373                                        gboolean on)
374 {
375         ModestWindowMgrPrivate *priv;
376         GList *win = NULL;
377
378         g_return_if_fail (MODEST_IS_WINDOW_MGR (self));
379
380         priv = MODEST_WINDOW_MGR_GET_PRIVATE (self);
381
382         /* If there is no change do nothing */
383         if (priv->fullscreen_mode == on)
384                 return;
385
386         priv->fullscreen_mode = on;
387
388         /* Update windows */
389         win = priv->window_list;
390         while (win) {
391                 if (on) {
392                         gtk_window_fullscreen (GTK_WINDOW (win->data));
393                         modest_window_show_toolbar (MODEST_WINDOW (win->data), 
394                                                     priv->show_toolbars_fullscreen);
395                 } else {
396                         gtk_window_unfullscreen (GTK_WINDOW (win->data));
397                         modest_window_show_toolbar (MODEST_WINDOW (win->data), 
398                                                     priv->show_toolbars);
399                 }
400                 win = g_list_next (win);
401         }
402 }
403
404 gboolean
405 modest_window_mgr_get_fullscreen_mode (ModestWindowMgr *self)
406 {
407         ModestWindowMgrPrivate *priv;
408
409         g_return_val_if_fail (MODEST_IS_WINDOW_MGR (self), FALSE);
410
411         priv = MODEST_WINDOW_MGR_GET_PRIVATE (self);
412
413         return priv->fullscreen_mode;
414 }
415
416 void 
417 modest_window_mgr_show_toolbars (ModestWindowMgr *self,
418                                  gboolean show_toolbars,
419                                  gboolean fullscreen)
420 {
421         ModestWindowMgrPrivate *priv;
422
423         g_return_if_fail (MODEST_IS_WINDOW_MGR (self));
424
425         priv = MODEST_WINDOW_MGR_GET_PRIVATE (self);
426
427         /* If nothing changes then return. Otherwise cache it, do not
428            save to GConf for the moment, it will be done when all
429            windows become unregistered in order to avoid unnecessary
430            ModestConf calls */
431         if (fullscreen) {
432                 if (priv->show_toolbars_fullscreen == show_toolbars)
433                         return;
434                 else
435                         priv->show_toolbars_fullscreen = show_toolbars;
436         } else {
437                 if (priv->show_toolbars == show_toolbars)
438                         return;
439                 else
440                         priv->show_toolbars = show_toolbars;
441         }
442
443         /* Apply now if the view mode is the right one */
444         if ((fullscreen && priv->fullscreen_mode) ||
445             (!fullscreen && !priv->fullscreen_mode)) {
446
447                 GList *win = priv->window_list;
448
449                 while (win) {
450                         modest_window_show_toolbar (MODEST_WINDOW (win->data),
451                                                     show_toolbars);
452                         win = g_list_next (win);
453                 }
454         }
455 }
456
457 ModestWindow*  
458 modest_window_mgr_get_main_window (ModestWindowMgr *self)
459 {
460         ModestWindowMgrPrivate *priv;
461
462         g_return_val_if_fail (MODEST_IS_WINDOW_MGR (self), NULL);
463
464         priv = MODEST_WINDOW_MGR_GET_PRIVATE (self);
465
466         return priv->main_window;
467 }
468
469 static void
470 on_nonhibernating_window_hide(GtkWidget *widget, gpointer user_data)
471 {
472         ModestWindowMgr *self = MODEST_WINDOW_MGR (user_data);
473         ModestWindowMgrPrivate *priv = MODEST_WINDOW_MGR_GET_PRIVATE (self);
474         
475         /* Forget this window,
476          * so hibernation will be allowed again if no windows are remembered: */
477         priv->windows_that_prevent_hibernation =
478                 g_slist_remove (priv->windows_that_prevent_hibernation, GTK_WINDOW(widget));
479 }
480
481 static void
482 on_nonhibernating_window_show(GtkWidget *widget, gpointer user_data)
483 {
484         ModestWindowMgr *self = MODEST_WINDOW_MGR (user_data);
485         ModestWindowMgrPrivate *priv = MODEST_WINDOW_MGR_GET_PRIVATE (self);
486         
487         GtkWindow *window = GTK_WINDOW (widget);
488         
489         priv->windows_that_prevent_hibernation = 
490                         g_slist_append (priv->windows_that_prevent_hibernation, window);
491         
492         /* Allow hibernation again when the window has been hidden: */
493         g_signal_connect (window, "hide", 
494                 G_CALLBACK (on_nonhibernating_window_hide), self);
495 }
496
497 void modest_window_mgr_prevent_hibernation_while_window_is_shown (ModestWindowMgr *self,
498                                                                   GtkWindow *window)
499 {
500         g_return_if_fail (MODEST_IS_WINDOW_MGR (self));
501         
502         if (GTK_WIDGET_VISIBLE(window)) {
503                 on_nonhibernating_window_show (GTK_WIDGET (window), self);
504         } else {
505                 /* Wait for it to be shown: */
506                 g_signal_connect (window, "show", 
507                         G_CALLBACK (on_nonhibernating_window_show), self);      
508         }
509 }
510
511 gboolean modest_window_mgr_get_hibernation_is_prevented (ModestWindowMgr *self)
512 {
513         g_return_val_if_fail (MODEST_IS_WINDOW_MGR (self), FALSE);
514         
515         ModestWindowMgrPrivate *priv = MODEST_WINDOW_MGR_GET_PRIVATE (self);
516         
517         /* Prevent hibernation if any open windows are currently 
518          * preventing hibernation: */
519         return (g_slist_length (priv->windows_that_prevent_hibernation) > 0);
520 }
521
522
523 void modest_window_mgr_save_state_for_all_windows (ModestWindowMgr *self)
524 {
525         g_return_if_fail (MODEST_IS_WINDOW_MGR (self));
526         
527         ModestWindowMgrPrivate *priv = MODEST_WINDOW_MGR_GET_PRIVATE (self);
528
529         /* Iterate over all windows */
530         GList *win = priv->window_list;
531         while (win) {
532                 ModestWindow *window = MODEST_WINDOW (win->data);
533                 if (window) {
534                         /* This calls the vfunc, 
535                          * so each window can do its own thing: */
536                         modest_window_save_state (window);
537                 }       
538                 
539                 win = g_list_next (win);
540         }
541 }