e8b583d160a1c70daf09b72146945701eea04f1c
[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 static gboolean on_window_destroy            (ModestWindow *window,
49                                               GdkEvent *event,
50                                               ModestWindowMgr *self);
51
52 /* list my signals  */
53 enum {
54         /* MY_SIGNAL_1, */
55         /* MY_SIGNAL_2, */
56         LAST_SIGNAL
57 };
58
59 typedef struct _ModestWindowMgrPrivate ModestWindowMgrPrivate;
60 struct _ModestWindowMgrPrivate {
61         GList        *window_list;
62         ModestWindow *main_window;
63         gboolean     fullscreen_mode;
64         gboolean     show_toolbars;
65         gboolean     show_toolbars_fullscreen;
66         
67         GSList       *windows_that_prevent_hibernation;
68         GHashTable   *destroy_handlers;
69 };
70 #define MODEST_WINDOW_MGR_GET_PRIVATE(o)      (G_TYPE_INSTANCE_GET_PRIVATE((o), \
71                                                MODEST_TYPE_WINDOW_MGR, \
72                                                ModestWindowMgrPrivate))
73 /* globals */
74 static GObjectClass *parent_class = NULL;
75
76 /* uncomment the following if you have defined any signals */
77 /* static guint signals[LAST_SIGNAL] = {0}; */
78
79 GType
80 modest_window_mgr_get_type (void)
81 {
82         static GType my_type = 0;
83         if (!my_type) {
84                 static const GTypeInfo my_info = {
85                         sizeof(ModestWindowMgrClass),
86                         NULL,           /* base init */
87                         NULL,           /* base finalize */
88                         (GClassInitFunc) modest_window_mgr_class_init,
89                         NULL,           /* class finalize */
90                         NULL,           /* class data */
91                         sizeof(ModestWindowMgr),
92                         1,              /* n_preallocs */
93                         (GInstanceInitFunc) modest_window_mgr_init,
94                         NULL
95                 };
96                 my_type = g_type_register_static (G_TYPE_OBJECT,
97                                                   "ModestWindowMgr",
98                                                   &my_info, 0);
99         }
100         return my_type;
101 }
102
103 static void
104 modest_window_mgr_class_init (ModestWindowMgrClass *klass)
105 {
106         GObjectClass *gobject_class;
107         gobject_class = (GObjectClass*) klass;
108
109         parent_class            = g_type_class_peek_parent (klass);
110         gobject_class->finalize = modest_window_mgr_finalize;
111
112         g_type_class_add_private (gobject_class, sizeof(ModestWindowMgrPrivate));
113 }
114
115 static void
116 modest_window_mgr_init (ModestWindowMgr *obj)
117 {
118         ModestWindowMgrPrivate *priv;
119
120         priv = MODEST_WINDOW_MGR_GET_PRIVATE(obj);
121         priv->window_list = NULL;
122         priv->main_window = NULL;
123         priv->fullscreen_mode = FALSE;
124
125         /* Could not initialize it from gconf, singletons are not
126            ready yet */
127         priv->show_toolbars = FALSE;
128         priv->show_toolbars_fullscreen = FALSE;
129         priv->destroy_handlers = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_free);
130 }
131
132 static void
133 modest_window_mgr_finalize (GObject *obj)
134 {
135         ModestWindowMgrPrivate *priv = MODEST_WINDOW_MGR_GET_PRIVATE(obj);
136
137         if (priv->window_list) {
138                 GList *iter = priv->window_list;
139                 /* unregister pending windows */
140                 while (iter) {
141                         modest_window_mgr_unregister_window (MODEST_WINDOW_MGR (obj), 
142                                                              MODEST_WINDOW (iter->data));
143                         iter = g_list_next (iter);
144                 }
145                 g_list_free (priv->window_list);
146                 priv->window_list = NULL;
147         }
148
149         /* Free the hash table with the handlers */
150         if (priv->destroy_handlers) {
151                 g_hash_table_destroy (priv->destroy_handlers);
152                 priv->destroy_handlers = NULL;
153         }
154
155         /* Do not unref priv->main_window because it does not hold a
156            new reference */
157
158         G_OBJECT_CLASS(parent_class)->finalize (obj);
159 }
160
161 ModestWindowMgr*
162 modest_window_mgr_new (void)
163 {
164         return MODEST_WINDOW_MGR(g_object_new(MODEST_TYPE_WINDOW_MGR, NULL));
165 }
166
167 void 
168 modest_window_mgr_register_window (ModestWindowMgr *self, 
169                                    ModestWindow *window)
170 {
171         static gboolean first_time = TRUE;
172         GList *win;
173         gboolean show;
174         ModestWindowMgrPrivate *priv;
175         gint *handler_id;
176
177         g_return_if_fail (MODEST_IS_WINDOW_MGR (self));
178         g_return_if_fail (MODEST_IS_WINDOW (window));
179
180         priv = MODEST_WINDOW_MGR_GET_PRIVATE (self);
181
182         win = g_list_find (priv->window_list, window);
183         if (win) {
184                 g_warning ("Trying to register an already registered window");
185                 return;
186         }
187
188         /* Check that it's not a second main window */
189         if (MODEST_IS_MAIN_WINDOW (window)) {
190                 if (priv->main_window) {
191                         g_warning ("Trying to register a second main window");
192                         return;
193                 } else {
194                         priv->main_window = window;
195                 }
196         }
197
198         /* Add to list. Keep a reference to the window */
199         g_object_ref (window);
200         priv->window_list = g_list_prepend (priv->window_list, window);
201
202         /* Listen to object destruction */
203         handler_id = g_malloc0 (sizeof (gint));
204         *handler_id = g_signal_connect (window, "delete-event", G_CALLBACK (on_window_destroy), self);
205 /*      *handler_id = g_signal_connect (window, "destroy", G_CALLBACK (on_window_destroy), self); */
206         g_hash_table_insert (priv->destroy_handlers, window, handler_id);
207
208         /* Put into fullscreen if needed */
209         if (priv->fullscreen_mode)
210                 gtk_window_fullscreen (GTK_WINDOW (window));
211
212         /* Fill caches */
213         if (first_time) {
214                 ModestConf *conf = modest_runtime_get_conf ();
215                 priv->show_toolbars = 
216                         modest_conf_get_bool (conf, MODEST_CONF_SHOW_TOOLBAR, NULL);
217                 priv->show_toolbars_fullscreen = 
218                         modest_conf_get_bool (conf, MODEST_CONF_SHOW_TOOLBAR_FULLSCREEN, NULL);
219                 first_time = FALSE;
220         }
221
222         /* Show/hide toolbar */
223         if (priv->fullscreen_mode)
224                 show = priv->show_toolbars_fullscreen;
225         else
226                 show = priv->show_toolbars;
227         modest_window_show_toolbar (window, show);
228 }
229
230 /* static void */
231 /* on_window_destroy (ModestWindow *window, ModestWindowMgr *self) */
232 static gboolean
233 on_window_destroy (ModestWindow *window, 
234                    GdkEvent *event,
235                    ModestWindowMgr *self)
236 {
237         /* Specific stuff first */
238         if (MODEST_IS_MAIN_WINDOW (window)) {
239                 ModestWindowMgrPrivate *priv;
240                 priv = MODEST_WINDOW_MGR_GET_PRIVATE (self);
241
242                 /* If more than one window already opened */
243                 if (g_list_length (priv->window_list) > 1) {
244
245                         /* If the user wants to close all the windows */
246                         if (modest_main_window_close_all (MODEST_MAIN_WINDOW (window))) {
247                                 GList *iter = priv->window_list;
248                                 do {
249                                         if (iter->data != window) {
250                                                 GList *tmp = iter->next;
251                                                 on_window_destroy (MODEST_WINDOW (iter->data),
252                                                                    event,
253                                                                    self);
254                                                 iter = tmp;
255                                         } else {
256                                                 iter = g_list_next (iter);
257                                         }
258                                 } while (iter);
259                         }
260                 }
261         }
262         else {
263                 if (MODEST_IS_MSG_EDIT_WINDOW (window)) {
264                         gboolean sent = FALSE;
265                         gint response = GTK_RESPONSE_ACCEPT;
266                         sent = modest_msg_edit_window_get_sent (MODEST_MSG_EDIT_WINDOW (window));
267                         /* Save currently edited message to Drafts if it was not sent */
268                         if (!sent && modest_msg_edit_window_is_modified (MODEST_MSG_EDIT_WINDOW (window))) {
269                                 
270                                 response =
271                                         modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
272                                                                                  _("mcen_nc_no_email_message_modified_save_changes"));
273                                 /* Save to drafts */
274                                 if (response != GTK_RESPONSE_CANCEL)                            
275                                         modest_ui_actions_on_save_to_drafts (NULL, MODEST_MSG_EDIT_WINDOW (window));
276                                 
277                         }
278                 }
279         }
280
281         /* Unregister window */
282         modest_window_mgr_unregister_window (self, window);
283         
284         return FALSE;
285 }
286
287 void 
288 modest_window_mgr_unregister_window (ModestWindowMgr *self, 
289                                      ModestWindow *window)
290 {
291         GList *win;
292         ModestWindowMgrPrivate *priv;
293         gint *tmp, handler_id;
294
295         g_return_if_fail (MODEST_IS_WINDOW_MGR (self));
296         g_return_if_fail (MODEST_IS_WINDOW (window));
297
298         priv = MODEST_WINDOW_MGR_GET_PRIVATE (self);
299
300         win = g_list_find (priv->window_list, window);
301         if (!win) {
302                 g_warning ("Trying to unregister a window that has not being registered yet");
303                 return;
304         }
305
306         /* If it's the main window unset it */
307         if (priv->main_window == window)
308                 priv->main_window = NULL;
309
310         /* Save state */
311         modest_window_save_state (window);
312
313         /* Remove from list & hash table */
314         priv->window_list = g_list_remove_link (priv->window_list, win);
315         tmp = g_hash_table_lookup (priv->destroy_handlers, window);
316         handler_id = *tmp;
317         g_hash_table_remove (priv->destroy_handlers, window);
318
319         /* Remove the reference to the window. Disconnect also the
320            delete-event handler, we won't need it anymore */
321         g_signal_handler_disconnect (window, handler_id);
322         gtk_widget_destroy (win->data);
323
324         /* If there are no more windows registered then exit program */
325         if (priv->window_list == NULL) {
326                 ModestConf *conf = modest_runtime_get_conf ();
327
328                 /* Save show toolbar status */
329                 modest_conf_set_bool (conf, MODEST_CONF_SHOW_TOOLBAR_FULLSCREEN, 
330                                       priv->show_toolbars_fullscreen, NULL);
331                 modest_conf_set_bool (conf, MODEST_CONF_SHOW_TOOLBAR, 
332                                       priv->show_toolbars, NULL);
333
334                 /* Quit main loop */
335                 gtk_main_quit ();
336         }
337 }
338
339 static gint
340 compare_msguids (ModestWindow *win,
341                  const gchar *uid)
342 {
343         const gchar *msg_uid;
344
345         if (!MODEST_IS_MSG_VIEW_WINDOW (win))
346                 return 1;
347
348         /* Get message uid from msg window */
349         msg_uid = modest_msg_view_window_get_message_uid (MODEST_MSG_VIEW_WINDOW (win));
350
351         if (msg_uid && uid &&!strcmp (msg_uid, uid))
352                 return 0;
353         else
354                 return 1;
355 }
356
357 ModestWindow*  
358 modest_window_mgr_find_window_by_header (ModestWindowMgr *self, 
359                                          TnyHeader *header)
360 {
361         ModestWindowMgrPrivate *priv;
362         GList *win = NULL;
363         gchar *msg_uid;
364
365         g_return_val_if_fail (MODEST_IS_WINDOW_MGR (self), NULL);
366         g_return_val_if_fail (TNY_IS_HEADER (header), NULL);
367
368         priv = MODEST_WINDOW_MGR_GET_PRIVATE (self);
369         msg_uid = modest_tny_folder_get_header_unique_id (header);
370
371         /* Look for the window */
372         if (priv->window_list)
373                 win = g_list_find_custom (priv->window_list, 
374                                           msg_uid, 
375                                           (GCompareFunc) compare_msguids);
376         /* Free */
377         g_free (msg_uid);
378
379         /* Return the window */
380         if (win)
381                 return win->data;
382         else 
383                 return NULL;
384 }
385
386 void
387 modest_window_mgr_set_fullscreen_mode (ModestWindowMgr *self,
388                                        gboolean on)
389 {
390         ModestWindowMgrPrivate *priv;
391         GList *win = NULL;
392
393         g_return_if_fail (MODEST_IS_WINDOW_MGR (self));
394
395         priv = MODEST_WINDOW_MGR_GET_PRIVATE (self);
396
397         /* If there is no change do nothing */
398         if (priv->fullscreen_mode == on)
399                 return;
400
401         priv->fullscreen_mode = on;
402
403         /* Update windows */
404         win = priv->window_list;
405         while (win) {
406                 if (on) {
407                         gtk_window_fullscreen (GTK_WINDOW (win->data));
408                         modest_window_show_toolbar (MODEST_WINDOW (win->data), 
409                                                     priv->show_toolbars_fullscreen);
410                 } else {
411                         gtk_window_unfullscreen (GTK_WINDOW (win->data));
412                         modest_window_show_toolbar (MODEST_WINDOW (win->data), 
413                                                     priv->show_toolbars);
414                 }
415                 win = g_list_next (win);
416         }
417 }
418
419 gboolean
420 modest_window_mgr_get_fullscreen_mode (ModestWindowMgr *self)
421 {
422         ModestWindowMgrPrivate *priv;
423
424         g_return_val_if_fail (MODEST_IS_WINDOW_MGR (self), FALSE);
425
426         priv = MODEST_WINDOW_MGR_GET_PRIVATE (self);
427
428         return priv->fullscreen_mode;
429 }
430
431 void 
432 modest_window_mgr_show_toolbars (ModestWindowMgr *self,
433                                  gboolean show_toolbars,
434                                  gboolean fullscreen)
435 {
436         ModestWindowMgrPrivate *priv;
437
438         g_return_if_fail (MODEST_IS_WINDOW_MGR (self));
439
440         priv = MODEST_WINDOW_MGR_GET_PRIVATE (self);
441
442         /* If nothing changes then return. Otherwise cache it, do not
443            save to GConf for the moment, it will be done when all
444            windows become unregistered in order to avoid unnecessary
445            ModestConf calls */
446         if (fullscreen) {
447                 if (priv->show_toolbars_fullscreen == show_toolbars)
448                         return;
449                 else
450                         priv->show_toolbars_fullscreen = show_toolbars;
451         } else {
452                 if (priv->show_toolbars == show_toolbars)
453                         return;
454                 else
455                         priv->show_toolbars = show_toolbars;
456         }
457
458         /* Apply now if the view mode is the right one */
459         if ((fullscreen && priv->fullscreen_mode) ||
460             (!fullscreen && !priv->fullscreen_mode)) {
461
462                 GList *win = priv->window_list;
463
464                 while (win) {
465                         modest_window_show_toolbar (MODEST_WINDOW (win->data),
466                                                     show_toolbars);
467                         win = g_list_next (win);
468                 }
469         }
470 }
471
472 ModestWindow*  
473 modest_window_mgr_get_main_window (ModestWindowMgr *self)
474 {
475         ModestWindowMgrPrivate *priv;
476
477         g_return_val_if_fail (MODEST_IS_WINDOW_MGR (self), NULL);
478
479         priv = MODEST_WINDOW_MGR_GET_PRIVATE (self);
480
481         return priv->main_window;
482 }
483
484 static void
485 on_nonhibernating_window_hide(GtkWidget *widget, gpointer user_data)
486 {
487         ModestWindowMgr *self = MODEST_WINDOW_MGR (user_data);
488         ModestWindowMgrPrivate *priv = MODEST_WINDOW_MGR_GET_PRIVATE (self);
489         
490         /* Forget this window,
491          * so hibernation will be allowed again if no windows are remembered: */
492         priv->windows_that_prevent_hibernation =
493                 g_slist_remove (priv->windows_that_prevent_hibernation, GTK_WINDOW(widget));
494 }
495
496 static void
497 on_nonhibernating_window_show(GtkWidget *widget, gpointer user_data)
498 {
499         ModestWindowMgr *self = MODEST_WINDOW_MGR (user_data);
500         ModestWindowMgrPrivate *priv = MODEST_WINDOW_MGR_GET_PRIVATE (self);
501         
502         GtkWindow *window = GTK_WINDOW (widget);
503         
504         priv->windows_that_prevent_hibernation = 
505                         g_slist_append (priv->windows_that_prevent_hibernation, window);
506         
507         /* Allow hibernation again when the window has been hidden: */
508         g_signal_connect (window, "hide", 
509                 G_CALLBACK (on_nonhibernating_window_hide), self);
510 }
511
512 void modest_window_mgr_prevent_hibernation_while_window_is_shown (ModestWindowMgr *self,
513                                                                   GtkWindow *window)
514 {
515         g_return_if_fail (MODEST_IS_WINDOW_MGR (self));
516         
517         if (GTK_WIDGET_VISIBLE(window)) {
518                 on_nonhibernating_window_show (GTK_WIDGET (window), self);
519         } else {
520                 /* Wait for it to be shown: */
521                 g_signal_connect (window, "show", 
522                         G_CALLBACK (on_nonhibernating_window_show), self);      
523         }
524 }
525
526 gboolean modest_window_mgr_get_hibernation_is_prevented (ModestWindowMgr *self)
527 {
528         g_return_val_if_fail (MODEST_IS_WINDOW_MGR (self), FALSE);
529         
530         ModestWindowMgrPrivate *priv = MODEST_WINDOW_MGR_GET_PRIVATE (self);
531         
532         /* Prevent hibernation if any open windows are currently 
533          * preventing hibernation: */
534         return (g_slist_length (priv->windows_that_prevent_hibernation) > 0);
535 }
536
537
538 void modest_window_mgr_save_state_for_all_windows (ModestWindowMgr *self)
539 {
540         g_return_if_fail (MODEST_IS_WINDOW_MGR (self));
541         
542         ModestWindowMgrPrivate *priv = MODEST_WINDOW_MGR_GET_PRIVATE (self);
543
544         /* Iterate over all windows */
545         GList *win = priv->window_list;
546         while (win) {
547                 ModestWindow *window = MODEST_WINDOW (win->data);
548                 if (window) {
549                         /* This calls the vfunc, 
550                          * so each window can do its own thing: */
551                         modest_window_save_state (window);
552                 }       
553                 
554                 win = g_list_next (win);
555         }
556 }