93fce0c4b6b7e48197dab9c3d8f8a95442e5434d
[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         /* Remove from list. Remove the reference to the window */
269         g_object_unref (win->data);
270         priv->window_list = g_list_remove_link (priv->window_list, win);
271
272         /* If there are no more windows registered then exit program */
273         if (priv->window_list == NULL) {
274                 ModestConf *conf = modest_runtime_get_conf ();
275
276                 /* Save show toolbar status */
277                 modest_conf_set_bool (conf, MODEST_CONF_SHOW_TOOLBAR_FULLSCREEN, 
278                                       priv->show_toolbars_fullscreen, NULL);
279                 modest_conf_set_bool (conf, MODEST_CONF_SHOW_TOOLBAR, 
280                                       priv->show_toolbars, NULL);
281
282                 /* Quit main loop */
283                 gtk_main_quit ();
284         }
285 }
286
287 static gint
288 compare_msguids (ModestWindow *win,
289                  const gchar *uid)
290 {
291         const gchar *msg_uid;
292
293         if (!MODEST_IS_MSG_VIEW_WINDOW (win))
294                 return 1;
295
296         /* Get message uid from msg window */
297         msg_uid = modest_msg_view_window_get_message_uid (MODEST_MSG_VIEW_WINDOW (win));
298
299         if (msg_uid && !strcmp (msg_uid, uid))
300                 return 0;
301         else
302                 return 1;
303 }
304
305 ModestWindow*  
306 modest_window_mgr_find_window_by_header (ModestWindowMgr *self, 
307                                          TnyHeader *header)
308 {
309         ModestWindowMgrPrivate *priv;
310         GList *win = NULL;
311         gchar *msg_uid;
312
313         g_return_val_if_fail (MODEST_IS_WINDOW_MGR (self), NULL);
314         g_return_val_if_fail (TNY_IS_HEADER (header), NULL);
315
316         priv = MODEST_WINDOW_MGR_GET_PRIVATE (self);
317         msg_uid = modest_tny_folder_get_header_unique_id (header);
318
319         /* Look for the window */
320         if (priv->window_list)
321                 win = g_list_find_custom (priv->window_list, 
322                                           msg_uid, 
323                                           (GCompareFunc) compare_msguids);
324         /* Free */
325         g_free (msg_uid);
326
327         /* Return the window */
328         if (win)
329                 return win->data;
330         else 
331                 return NULL;
332 }
333
334 void
335 modest_window_mgr_set_fullscreen_mode (ModestWindowMgr *self,
336                                        gboolean on)
337 {
338         ModestWindowMgrPrivate *priv;
339         GList *win = NULL;
340
341         g_return_if_fail (MODEST_IS_WINDOW_MGR (self));
342
343         priv = MODEST_WINDOW_MGR_GET_PRIVATE (self);
344
345         /* If there is no change do nothing */
346         if (priv->fullscreen_mode == on)
347                 return;
348
349         priv->fullscreen_mode = on;
350
351         /* Update windows */
352         win = priv->window_list;
353         while (win) {
354                 if (on) {
355                         gtk_window_fullscreen (GTK_WINDOW (win->data));
356                         modest_window_show_toolbar (MODEST_WINDOW (win->data), 
357                                                     priv->show_toolbars_fullscreen);
358                 } else {
359                         gtk_window_unfullscreen (GTK_WINDOW (win->data));
360                         modest_window_show_toolbar (MODEST_WINDOW (win->data), 
361                                                     priv->show_toolbars);
362                 }
363                 win = g_list_next (win);
364         }
365 }
366
367 gboolean
368 modest_window_mgr_get_fullscreen_mode (ModestWindowMgr *self)
369 {
370         ModestWindowMgrPrivate *priv;
371
372         g_return_val_if_fail (MODEST_IS_WINDOW_MGR (self), FALSE);
373
374         priv = MODEST_WINDOW_MGR_GET_PRIVATE (self);
375
376         return priv->fullscreen_mode;
377 }
378
379 void 
380 modest_window_mgr_show_toolbars (ModestWindowMgr *self,
381                                  gboolean show_toolbars,
382                                  gboolean fullscreen)
383 {
384         ModestWindowMgrPrivate *priv;
385
386         g_return_if_fail (MODEST_IS_WINDOW_MGR (self));
387
388         priv = MODEST_WINDOW_MGR_GET_PRIVATE (self);
389
390         /* If nothing changes then return. Otherwise cache it, do not
391            save to GConf for the moment, it will be done when all
392            windows become unregistered in order to avoid unnecessary
393            ModestConf calls */
394         if (fullscreen) {
395                 if (priv->show_toolbars_fullscreen == show_toolbars)
396                         return;
397                 else
398                         priv->show_toolbars_fullscreen = show_toolbars;
399         } else {
400                 if (priv->show_toolbars == show_toolbars)
401                         return;
402                 else
403                         priv->show_toolbars = show_toolbars;
404         }
405
406         /* Apply now if the view mode is the right one */
407         if ((fullscreen && priv->fullscreen_mode) ||
408             (!fullscreen && !priv->fullscreen_mode)) {
409
410                 GList *win = priv->window_list;
411
412                 while (win) {
413                         modest_window_show_toolbar (MODEST_WINDOW (win->data),
414                                                     show_toolbars);
415                         win = g_list_next (win);
416                 }
417         }
418 }
419
420 ModestWindow*  
421 modest_window_mgr_get_main_window (ModestWindowMgr *self)
422 {
423         ModestWindowMgrPrivate *priv;
424
425         g_return_val_if_fail (MODEST_IS_WINDOW_MGR (self), NULL);
426
427         priv = MODEST_WINDOW_MGR_GET_PRIVATE (self);
428
429         return priv->main_window;
430 }
431
432 static void
433 on_nonhibernating_window_hide(GtkWidget *widget, gpointer user_data)
434 {
435         ModestWindowMgr *self = MODEST_WINDOW_MGR (user_data);
436         ModestWindowMgrPrivate *priv = MODEST_WINDOW_MGR_GET_PRIVATE (self);
437         
438         /* Forget this window,
439          * so hibernation will be allowed again if no windows are remembered: */
440         priv->windows_that_prevent_hibernation =
441                 g_slist_remove (priv->windows_that_prevent_hibernation, GTK_WINDOW(widget));
442 }
443
444 static void
445 on_nonhibernating_window_show(GtkWidget *widget, gpointer user_data)
446 {
447         ModestWindowMgr *self = MODEST_WINDOW_MGR (user_data);
448         ModestWindowMgrPrivate *priv = MODEST_WINDOW_MGR_GET_PRIVATE (self);
449         
450         GtkWindow *window = GTK_WINDOW (widget);
451         
452         priv->windows_that_prevent_hibernation = 
453                         g_slist_append (priv->windows_that_prevent_hibernation, window);
454         
455         /* Allow hibernation again when the window has been hidden: */
456         g_signal_connect (window, "hide", 
457                 G_CALLBACK (on_nonhibernating_window_hide), self);
458 }
459
460 void modest_window_mgr_prevent_hibernation_while_window_is_shown (ModestWindowMgr *self, GtkWindow *window)
461 {
462         g_return_if_fail (MODEST_IS_WINDOW_MGR (self));
463         
464         if (GTK_WIDGET_VISIBLE(window)) {
465                 on_nonhibernating_window_show (GTK_WIDGET (window), self);
466         }
467         else
468         {
469                 /* Wait for it to be shown: */
470                 g_signal_connect (window, "show", 
471                         G_CALLBACK (on_nonhibernating_window_show), self);      
472         }
473 }
474
475 gboolean modest_window_mgr_get_hibernation_is_prevented (ModestWindowMgr *self)
476 {
477         g_return_val_if_fail (MODEST_IS_WINDOW_MGR (self), FALSE);
478         
479         ModestWindowMgrPrivate *priv = MODEST_WINDOW_MGR_GET_PRIVATE (self);
480         
481         /* Prevent hibernation if any open windows are currently 
482          * preventing hibernation: */
483         return (g_slist_length (priv->windows_that_prevent_hibernation) > 0);
484 }
485
486
487 void modest_window_mgr_save_state_for_all_windows (ModestWindowMgr *self)
488 {
489         g_return_if_fail (MODEST_IS_WINDOW_MGR (self));
490         
491         ModestWindowMgrPrivate *priv = MODEST_WINDOW_MGR_GET_PRIVATE (self);
492
493         /* Iterate over all windows */
494         GList *win = priv->window_list;
495         while (win) {
496                 ModestWindow *window = MODEST_WINDOW (win->data);
497                 if (window) {
498                         /* This calls the vfunc, 
499                          * so each window can do its own thing: */
500                         modest_window_save_state (window);
501                 }       
502                 
503                 win = g_list_next (win);
504         }
505 }