Fixes: NB#61843
[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. We need to block the
320            destroy event handler to avoid recursive calls */
321         g_signal_handler_block (window, handler_id);
322         gtk_widget_destroy (win->data);
323         if (G_IS_OBJECT (window))
324                 g_signal_handler_unblock (window, handler_id);
325
326         /* If there are no more windows registered then exit program */
327         if (priv->window_list == NULL) {
328                 ModestConf *conf = modest_runtime_get_conf ();
329
330                 /* Save show toolbar status */
331                 modest_conf_set_bool (conf, MODEST_CONF_SHOW_TOOLBAR_FULLSCREEN, 
332                                       priv->show_toolbars_fullscreen, NULL);
333                 modest_conf_set_bool (conf, MODEST_CONF_SHOW_TOOLBAR, 
334                                       priv->show_toolbars, NULL);
335
336                 /* Quit main loop */
337                 gtk_main_quit ();
338         }
339 }
340
341 static gint
342 compare_msguids (ModestWindow *win,
343                  const gchar *uid)
344 {
345         const gchar *msg_uid;
346
347         if (!MODEST_IS_MSG_VIEW_WINDOW (win))
348                 return 1;
349
350         /* Get message uid from msg window */
351         msg_uid = modest_msg_view_window_get_message_uid (MODEST_MSG_VIEW_WINDOW (win));
352
353         if (msg_uid && uid &&!strcmp (msg_uid, uid))
354                 return 0;
355         else
356                 return 1;
357 }
358
359 ModestWindow*  
360 modest_window_mgr_find_window_by_header (ModestWindowMgr *self, 
361                                          TnyHeader *header)
362 {
363         ModestWindowMgrPrivate *priv;
364         GList *win = NULL;
365         gchar *msg_uid;
366
367         g_return_val_if_fail (MODEST_IS_WINDOW_MGR (self), NULL);
368         g_return_val_if_fail (TNY_IS_HEADER (header), NULL);
369
370         priv = MODEST_WINDOW_MGR_GET_PRIVATE (self);
371         msg_uid = modest_tny_folder_get_header_unique_id (header);
372
373         /* Look for the window */
374         if (priv->window_list)
375                 win = g_list_find_custom (priv->window_list, 
376                                           msg_uid, 
377                                           (GCompareFunc) compare_msguids);
378         /* Free */
379         g_free (msg_uid);
380
381         /* Return the window */
382         if (win)
383                 return win->data;
384         else 
385                 return NULL;
386 }
387
388 void
389 modest_window_mgr_set_fullscreen_mode (ModestWindowMgr *self,
390                                        gboolean on)
391 {
392         ModestWindowMgrPrivate *priv;
393         GList *win = NULL;
394
395         g_return_if_fail (MODEST_IS_WINDOW_MGR (self));
396
397         priv = MODEST_WINDOW_MGR_GET_PRIVATE (self);
398
399         /* If there is no change do nothing */
400         if (priv->fullscreen_mode == on)
401                 return;
402
403         priv->fullscreen_mode = on;
404
405         /* Update windows */
406         win = priv->window_list;
407         while (win) {
408                 if (on) {
409                         gtk_window_fullscreen (GTK_WINDOW (win->data));
410                         modest_window_show_toolbar (MODEST_WINDOW (win->data), 
411                                                     priv->show_toolbars_fullscreen);
412                 } else {
413                         gtk_window_unfullscreen (GTK_WINDOW (win->data));
414                         modest_window_show_toolbar (MODEST_WINDOW (win->data), 
415                                                     priv->show_toolbars);
416                 }
417                 win = g_list_next (win);
418         }
419 }
420
421 gboolean
422 modest_window_mgr_get_fullscreen_mode (ModestWindowMgr *self)
423 {
424         ModestWindowMgrPrivate *priv;
425
426         g_return_val_if_fail (MODEST_IS_WINDOW_MGR (self), FALSE);
427
428         priv = MODEST_WINDOW_MGR_GET_PRIVATE (self);
429
430         return priv->fullscreen_mode;
431 }
432
433 void 
434 modest_window_mgr_show_toolbars (ModestWindowMgr *self,
435                                  gboolean show_toolbars,
436                                  gboolean fullscreen)
437 {
438         ModestWindowMgrPrivate *priv;
439
440         g_return_if_fail (MODEST_IS_WINDOW_MGR (self));
441
442         priv = MODEST_WINDOW_MGR_GET_PRIVATE (self);
443
444         /* If nothing changes then return. Otherwise cache it, do not
445            save to GConf for the moment, it will be done when all
446            windows become unregistered in order to avoid unnecessary
447            ModestConf calls */
448         if (fullscreen) {
449                 if (priv->show_toolbars_fullscreen == show_toolbars)
450                         return;
451                 else
452                         priv->show_toolbars_fullscreen = show_toolbars;
453         } else {
454                 if (priv->show_toolbars == show_toolbars)
455                         return;
456                 else
457                         priv->show_toolbars = show_toolbars;
458         }
459
460         /* Apply now if the view mode is the right one */
461         if ((fullscreen && priv->fullscreen_mode) ||
462             (!fullscreen && !priv->fullscreen_mode)) {
463
464                 GList *win = priv->window_list;
465
466                 while (win) {
467                         modest_window_show_toolbar (MODEST_WINDOW (win->data),
468                                                     show_toolbars);
469                         win = g_list_next (win);
470                 }
471         }
472 }
473
474 ModestWindow*  
475 modest_window_mgr_get_main_window (ModestWindowMgr *self)
476 {
477         ModestWindowMgrPrivate *priv;
478
479         g_return_val_if_fail (MODEST_IS_WINDOW_MGR (self), NULL);
480
481         priv = MODEST_WINDOW_MGR_GET_PRIVATE (self);
482
483         return priv->main_window;
484 }
485
486 static void
487 on_nonhibernating_window_hide(GtkWidget *widget, gpointer user_data)
488 {
489         ModestWindowMgr *self = MODEST_WINDOW_MGR (user_data);
490         ModestWindowMgrPrivate *priv = MODEST_WINDOW_MGR_GET_PRIVATE (self);
491         
492         /* Forget this window,
493          * so hibernation will be allowed again if no windows are remembered: */
494         priv->windows_that_prevent_hibernation =
495                 g_slist_remove (priv->windows_that_prevent_hibernation, GTK_WINDOW(widget));
496 }
497
498 static void
499 on_nonhibernating_window_show(GtkWidget *widget, gpointer user_data)
500 {
501         ModestWindowMgr *self = MODEST_WINDOW_MGR (user_data);
502         ModestWindowMgrPrivate *priv = MODEST_WINDOW_MGR_GET_PRIVATE (self);
503         
504         GtkWindow *window = GTK_WINDOW (widget);
505         
506         priv->windows_that_prevent_hibernation = 
507                         g_slist_append (priv->windows_that_prevent_hibernation, window);
508         
509         /* Allow hibernation again when the window has been hidden: */
510         g_signal_connect (window, "hide", 
511                 G_CALLBACK (on_nonhibernating_window_hide), self);
512 }
513
514 void modest_window_mgr_prevent_hibernation_while_window_is_shown (ModestWindowMgr *self,
515                                                                   GtkWindow *window)
516 {
517         g_return_if_fail (MODEST_IS_WINDOW_MGR (self));
518         
519         if (GTK_WIDGET_VISIBLE(window)) {
520                 on_nonhibernating_window_show (GTK_WIDGET (window), self);
521         } else {
522                 /* Wait for it to be shown: */
523                 g_signal_connect (window, "show", 
524                         G_CALLBACK (on_nonhibernating_window_show), self);      
525         }
526 }
527
528 gboolean modest_window_mgr_get_hibernation_is_prevented (ModestWindowMgr *self)
529 {
530         g_return_val_if_fail (MODEST_IS_WINDOW_MGR (self), FALSE);
531         
532         ModestWindowMgrPrivate *priv = MODEST_WINDOW_MGR_GET_PRIVATE (self);
533         
534         /* Prevent hibernation if any open windows are currently 
535          * preventing hibernation: */
536         return (g_slist_length (priv->windows_that_prevent_hibernation) > 0);
537 }
538
539
540 void modest_window_mgr_save_state_for_all_windows (ModestWindowMgr *self)
541 {
542         g_return_if_fail (MODEST_IS_WINDOW_MGR (self));
543         
544         ModestWindowMgrPrivate *priv = MODEST_WINDOW_MGR_GET_PRIVATE (self);
545
546         /* Iterate over all windows */
547         GList *win = priv->window_list;
548         while (win) {
549                 ModestWindow *window = MODEST_WINDOW (win->data);
550                 if (window) {
551                         /* This calls the vfunc, 
552                          * so each window can do its own thing: */
553                         modest_window_save_state (window);
554                 }       
555                 
556                 win = g_list_next (win);
557         }
558 }