* Added an attribute sent to the msg edit window that specifies that the mail has...
[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                         gboolean sent;
255
256                         sent = modest_msg_edit_window_get_sent (MODEST_MSG_EDIT_WINDOW (window));
257                         /* Save currently edited message to Drafts if it was not sent */
258                         if (!sent && modest_msg_edit_window_is_modified (MODEST_MSG_EDIT_WINDOW (window))) {
259                                 gint response = 
260                                         modest_platform_run_confirmation_dialog (GTK_WINDOW (self), 
261                                                                                  _("mcen_nc_no_email_message_modified_save_changes"));
262                                 if (response != GTK_RESPONSE_CANCEL) {
263                                         modest_ui_actions_on_save_to_drafts (NULL, MODEST_MSG_EDIT_WINDOW (window));
264                                 }
265                         }
266                 }
267         }
268
269         /* Unregister window */
270         modest_window_mgr_unregister_window (self, window);
271 }
272
273 void 
274 modest_window_mgr_unregister_window (ModestWindowMgr *self, 
275                                      ModestWindow *window)
276 {
277         GList *win;
278         ModestWindowMgrPrivate *priv;
279         gint *tmp, handler_id;
280
281         g_return_if_fail (MODEST_IS_WINDOW_MGR (self));
282         g_return_if_fail (MODEST_IS_WINDOW (window));
283
284         priv = MODEST_WINDOW_MGR_GET_PRIVATE (self);
285
286         win = g_list_find (priv->window_list, window);
287         if (!win) {
288                 g_warning ("Trying to unregister a window that has not being registered yet");
289                 return;
290         }
291
292         /* If it's the main window unset it */
293         if (priv->main_window == window)
294                 priv->main_window = NULL;
295
296         /* Save state */
297         modest_window_save_state (window);
298
299         /* Remove from list & hash table */
300         priv->window_list = g_list_remove_link (priv->window_list, win);
301         tmp = g_hash_table_lookup (priv->destroy_handlers, window);
302         handler_id = *tmp;
303         g_hash_table_remove (priv->destroy_handlers, window);
304
305         /* Remove the reference to the window. We need to block the
306            destroy event handler to avoid recursive calls */
307         g_signal_handler_block (window, handler_id);
308         gtk_widget_destroy (win->data);
309         if (G_IS_OBJECT (window))
310                 g_signal_handler_unblock (window, handler_id);
311
312         /* If there are no more windows registered then exit program */
313         if (priv->window_list == NULL) {
314                 ModestConf *conf = modest_runtime_get_conf ();
315
316                 /* Save show toolbar status */
317                 modest_conf_set_bool (conf, MODEST_CONF_SHOW_TOOLBAR_FULLSCREEN, 
318                                       priv->show_toolbars_fullscreen, NULL);
319                 modest_conf_set_bool (conf, MODEST_CONF_SHOW_TOOLBAR, 
320                                       priv->show_toolbars, NULL);
321
322                 /* Quit main loop */
323                 gtk_main_quit ();
324         }
325 }
326
327 static gint
328 compare_msguids (ModestWindow *win,
329                  const gchar *uid)
330 {
331         const gchar *msg_uid;
332
333         if (!MODEST_IS_MSG_VIEW_WINDOW (win))
334                 return 1;
335
336         /* Get message uid from msg window */
337         msg_uid = modest_msg_view_window_get_message_uid (MODEST_MSG_VIEW_WINDOW (win));
338
339         if (msg_uid && uid &&!strcmp (msg_uid, uid))
340                 return 0;
341         else
342                 return 1;
343 }
344
345 ModestWindow*  
346 modest_window_mgr_find_window_by_header (ModestWindowMgr *self, 
347                                          TnyHeader *header)
348 {
349         ModestWindowMgrPrivate *priv;
350         GList *win = NULL;
351         gchar *msg_uid;
352
353         g_return_val_if_fail (MODEST_IS_WINDOW_MGR (self), NULL);
354         g_return_val_if_fail (TNY_IS_HEADER (header), NULL);
355
356         priv = MODEST_WINDOW_MGR_GET_PRIVATE (self);
357         msg_uid = modest_tny_folder_get_header_unique_id (header);
358
359         /* Look for the window */
360         if (priv->window_list)
361                 win = g_list_find_custom (priv->window_list, 
362                                           msg_uid, 
363                                           (GCompareFunc) compare_msguids);
364         /* Free */
365         g_free (msg_uid);
366
367         /* Return the window */
368         if (win)
369                 return win->data;
370         else 
371                 return NULL;
372 }
373
374 void
375 modest_window_mgr_set_fullscreen_mode (ModestWindowMgr *self,
376                                        gboolean on)
377 {
378         ModestWindowMgrPrivate *priv;
379         GList *win = NULL;
380
381         g_return_if_fail (MODEST_IS_WINDOW_MGR (self));
382
383         priv = MODEST_WINDOW_MGR_GET_PRIVATE (self);
384
385         /* If there is no change do nothing */
386         if (priv->fullscreen_mode == on)
387                 return;
388
389         priv->fullscreen_mode = on;
390
391         /* Update windows */
392         win = priv->window_list;
393         while (win) {
394                 if (on) {
395                         gtk_window_fullscreen (GTK_WINDOW (win->data));
396                         modest_window_show_toolbar (MODEST_WINDOW (win->data), 
397                                                     priv->show_toolbars_fullscreen);
398                 } else {
399                         gtk_window_unfullscreen (GTK_WINDOW (win->data));
400                         modest_window_show_toolbar (MODEST_WINDOW (win->data), 
401                                                     priv->show_toolbars);
402                 }
403                 win = g_list_next (win);
404         }
405 }
406
407 gboolean
408 modest_window_mgr_get_fullscreen_mode (ModestWindowMgr *self)
409 {
410         ModestWindowMgrPrivate *priv;
411
412         g_return_val_if_fail (MODEST_IS_WINDOW_MGR (self), FALSE);
413
414         priv = MODEST_WINDOW_MGR_GET_PRIVATE (self);
415
416         return priv->fullscreen_mode;
417 }
418
419 void 
420 modest_window_mgr_show_toolbars (ModestWindowMgr *self,
421                                  gboolean show_toolbars,
422                                  gboolean fullscreen)
423 {
424         ModestWindowMgrPrivate *priv;
425
426         g_return_if_fail (MODEST_IS_WINDOW_MGR (self));
427
428         priv = MODEST_WINDOW_MGR_GET_PRIVATE (self);
429
430         /* If nothing changes then return. Otherwise cache it, do not
431            save to GConf for the moment, it will be done when all
432            windows become unregistered in order to avoid unnecessary
433            ModestConf calls */
434         if (fullscreen) {
435                 if (priv->show_toolbars_fullscreen == show_toolbars)
436                         return;
437                 else
438                         priv->show_toolbars_fullscreen = show_toolbars;
439         } else {
440                 if (priv->show_toolbars == show_toolbars)
441                         return;
442                 else
443                         priv->show_toolbars = show_toolbars;
444         }
445
446         /* Apply now if the view mode is the right one */
447         if ((fullscreen && priv->fullscreen_mode) ||
448             (!fullscreen && !priv->fullscreen_mode)) {
449
450                 GList *win = priv->window_list;
451
452                 while (win) {
453                         modest_window_show_toolbar (MODEST_WINDOW (win->data),
454                                                     show_toolbars);
455                         win = g_list_next (win);
456                 }
457         }
458 }
459
460 ModestWindow*  
461 modest_window_mgr_get_main_window (ModestWindowMgr *self)
462 {
463         ModestWindowMgrPrivate *priv;
464
465         g_return_val_if_fail (MODEST_IS_WINDOW_MGR (self), NULL);
466
467         priv = MODEST_WINDOW_MGR_GET_PRIVATE (self);
468
469         return priv->main_window;
470 }
471
472 static void
473 on_nonhibernating_window_hide(GtkWidget *widget, gpointer user_data)
474 {
475         ModestWindowMgr *self = MODEST_WINDOW_MGR (user_data);
476         ModestWindowMgrPrivate *priv = MODEST_WINDOW_MGR_GET_PRIVATE (self);
477         
478         /* Forget this window,
479          * so hibernation will be allowed again if no windows are remembered: */
480         priv->windows_that_prevent_hibernation =
481                 g_slist_remove (priv->windows_that_prevent_hibernation, GTK_WINDOW(widget));
482 }
483
484 static void
485 on_nonhibernating_window_show(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         GtkWindow *window = GTK_WINDOW (widget);
491         
492         priv->windows_that_prevent_hibernation = 
493                         g_slist_append (priv->windows_that_prevent_hibernation, window);
494         
495         /* Allow hibernation again when the window has been hidden: */
496         g_signal_connect (window, "hide", 
497                 G_CALLBACK (on_nonhibernating_window_hide), self);
498 }
499
500 void modest_window_mgr_prevent_hibernation_while_window_is_shown (ModestWindowMgr *self,
501                                                                   GtkWindow *window)
502 {
503         g_return_if_fail (MODEST_IS_WINDOW_MGR (self));
504         
505         if (GTK_WIDGET_VISIBLE(window)) {
506                 on_nonhibernating_window_show (GTK_WIDGET (window), self);
507         } else {
508                 /* Wait for it to be shown: */
509                 g_signal_connect (window, "show", 
510                         G_CALLBACK (on_nonhibernating_window_show), self);      
511         }
512 }
513
514 gboolean modest_window_mgr_get_hibernation_is_prevented (ModestWindowMgr *self)
515 {
516         g_return_val_if_fail (MODEST_IS_WINDOW_MGR (self), FALSE);
517         
518         ModestWindowMgrPrivate *priv = MODEST_WINDOW_MGR_GET_PRIVATE (self);
519         
520         /* Prevent hibernation if any open windows are currently 
521          * preventing hibernation: */
522         return (g_slist_length (priv->windows_that_prevent_hibernation) > 0);
523 }
524
525
526 void modest_window_mgr_save_state_for_all_windows (ModestWindowMgr *self)
527 {
528         g_return_if_fail (MODEST_IS_WINDOW_MGR (self));
529         
530         ModestWindowMgrPrivate *priv = MODEST_WINDOW_MGR_GET_PRIVATE (self);
531
532         /* Iterate over all windows */
533         GList *win = priv->window_list;
534         while (win) {
535                 ModestWindow *window = MODEST_WINDOW (win->data);
536                 if (window) {
537                         /* This calls the vfunc, 
538                          * so each window can do its own thing: */
539                         modest_window_save_state (window);
540                 }       
541                 
542                 win = g_list_next (win);
543         }
544 }