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