* Changes in the autotools stuff affecting a lot of platform dependent
[modest] / src / widgets / modest-window-mgr.c
index 5cbae89..fb88abf 100644 (file)
@@ -52,6 +52,9 @@ static gboolean on_modal_window_close    (GtkWidget *widget,
                                          GdkEvent *event,
                                          gpointer user_data);
 
+static void on_modal_dialog_destroy      (GtkObject *object,
+                                         gpointer   user_data);
+
 static void     on_modal_dialog_close    (GtkDialog *dialog,
                                          gint arg1,
                                          gpointer user_data);
@@ -81,6 +84,7 @@ struct _ModestWindowMgrPrivate {
        GSList       *preregistered_uids;
        GHashTable   *destroy_handlers;
        GHashTable   *viewer_handlers;
+       GSList       *window_state_uids;
        
        guint        closing_time;
 
@@ -89,6 +93,8 @@ struct _ModestWindowMgrPrivate {
        GtkWidget    *cached_editor;
        guint        idle_load_view_id;
        guint        idle_load_editor_id;
+
+       ModestWindow *current_top;
 };
 #define MODEST_WINDOW_MGR_GET_PRIVATE(o)      (G_TYPE_INSTANCE_GET_PRIVATE((o), \
                                                MODEST_TYPE_WINDOW_MGR, \
@@ -198,6 +204,8 @@ modest_window_mgr_init (ModestWindowMgr *obj)
        priv->banner_counter = 0;
        priv->main_window = NULL;
        priv->fullscreen_mode = FALSE;
+       priv->current_top = NULL;
+       priv->window_state_uids = NULL;
 
        priv->modal_windows = g_queue_new ();
        priv->queue_lock = g_mutex_new ();
@@ -240,13 +248,16 @@ modest_window_mgr_finalize (GObject *obj)
                priv->cached_editor = NULL;
        }
 
+       modest_signal_mgr_disconnect_all_and_destroy (priv->window_state_uids);
+       priv->window_state_uids = NULL;
+
        if (priv->window_list) {
                GList *iter = priv->window_list;
                /* unregister pending windows */
                while (iter) {
-                       modest_window_mgr_unregister_window (MODEST_WINDOW_MGR (obj), 
-                                                            MODEST_WINDOW (iter->data));
+                       ModestWindow *window = (ModestWindow *) iter->data;
                        iter = g_list_next (iter);
+                       modest_window_mgr_unregister_window (MODEST_WINDOW_MGR (obj), window);
                }
                g_list_free (priv->window_list);
                priv->window_list = NULL;
@@ -268,6 +279,7 @@ modest_window_mgr_finalize (GObject *obj)
        }
 
        modest_signal_mgr_disconnect_all_and_destroy (priv->modal_handler_uids);
+       priv->modal_handler_uids = NULL;
 
        if (priv->modal_windows) {
                g_mutex_lock (priv->queue_lock);
@@ -318,7 +330,7 @@ remove_uid (GSList *list, const gchar *uid)
        GSList *cursor = list, *start = list;
        
        if (!uid)
-               return FALSE;
+               return list;
        
        while (cursor) {
                GSList *next = g_slist_next (cursor);
@@ -413,37 +425,11 @@ modest_window_mgr_register_help_id (ModestWindowMgr *self, GtkWindow *win, const
 const gchar*
 modest_window_mgr_get_help_id (ModestWindowMgr *self, GtkWindow *win)
 {
-       const gchar* help_id = NULL;
-
        /* we don't need 'self', but for API consistency... */
-       g_return_val_if_fail (self && MODEST_IS_WINDOW_MGR(self), NULL);
-       
+       g_return_val_if_fail (self && MODEST_IS_WINDOW_MGR (self), NULL);
        g_return_val_if_fail (win, NULL);
-       g_return_val_if_fail (GTK_IS_WINDOW(win), NULL);
-       
-       if (MODEST_IS_MAIN_WINDOW (win)) {
-               GtkWidget *folder_view;
-               TnyFolderStore *folder_store;
-               
-               /* Get selected folder */
-               folder_view = modest_main_window_get_child_widget (MODEST_MAIN_WINDOW (win),
-                                                                  MODEST_MAIN_WINDOW_WIDGET_TYPE_FOLDER_VIEW);
-               folder_store = modest_folder_view_get_selected (MODEST_FOLDER_VIEW (folder_view));
-
-               /* Switch help_id */
-               if (folder_store && TNY_IS_FOLDER (folder_store)) {
-                       help_id = modest_tny_folder_get_help_id (TNY_FOLDER (folder_store));
-                       if (!help_id)
-                               g_warning ("%s: BUG: did not get a valid help_id", __FUNCTION__);
-               }
-               if (folder_store)
-                       g_object_unref (folder_store);
-       }
 
-       if (!help_id)
-               help_id = g_object_get_data (G_OBJECT(win), MODEST_WINDOW_HELP_ID_PARAM);
-               
-       return help_id;
+       return g_object_get_data (G_OBJECT(win), MODEST_WINDOW_HELP_ID_PARAM);
 }
 
 static gint
@@ -560,6 +546,64 @@ get_show_toolbar_key (GType window_type,
        return key;
 }
 
+#ifndef MODEST_TOOLKIT_GTK
+static void
+on_window_is_topmost (GObject    *gobject,
+                     GParamSpec *arg1,
+                     gpointer    user_data)
+{
+       ModestWindowMgr *self;
+       ModestWindowMgrPrivate *priv;
+       ModestWindow *win = (ModestWindow *) gobject;
+
+       g_return_if_fail (MODEST_IS_WINDOW_MGR (user_data));
+
+       self = MODEST_WINDOW_MGR (user_data);
+       priv = MODEST_WINDOW_MGR_GET_PRIVATE (self);
+
+       if (hildon_window_get_is_topmost (HILDON_WINDOW (win)))
+               priv->current_top = win;
+}
+
+#else
+static gboolean
+on_window_state_event (GtkWidget           *widget,
+                      GdkEventWindowState *event,
+                      gpointer             user_data)
+{
+       ModestWindowMgr *self;
+       ModestWindowMgrPrivate *priv;
+
+       g_return_val_if_fail (MODEST_IS_WINDOW_MGR (user_data), FALSE);
+
+       self = MODEST_WINDOW_MGR (user_data);
+       priv = MODEST_WINDOW_MGR_GET_PRIVATE (self);
+
+       MODEST_DEBUG_BLOCK (
+                           if (event->changed_mask & GDK_WINDOW_STATE_WITHDRAWN)
+                                   g_print ("GDK_WINDOW_STATE_WITHDRAWN\n");
+                           if (event->changed_mask & GDK_WINDOW_STATE_ICONIFIED)
+                                   g_print ("GDK_WINDOW_STATE_ICONIFIED\n");
+                           if (event->changed_mask & GDK_WINDOW_STATE_MAXIMIZED)
+                                   g_print ("GDK_WINDOW_STATE_MAXIMIZED\n");
+                           if (event->changed_mask & GDK_WINDOW_STATE_STICKY)
+                                   g_print ("GDK_WINDOW_STATE_STICKY\n");
+                           if (event->changed_mask & GDK_WINDOW_STATE_FULLSCREEN)
+                                   g_print ("GDK_WINDOW_STATE_FULLSCREEN\n");
+                           if (event->changed_mask & GDK_WINDOW_STATE_ABOVE)
+                                   g_print ("GDK_WINDOW_STATE_ABOVE\n");
+                           if (event->changed_mask & GDK_WINDOW_STATE_BELOW)
+                                   g_print ("GDK_WINDOW_STATE_BELOW\n");
+                           );
+       if (event->changed_mask & GDK_WINDOW_STATE_WITHDRAWN ||
+           event->changed_mask & GDK_WINDOW_STATE_ABOVE) {
+               priv->current_top = MODEST_WINDOW (widget);
+       }
+       
+       return FALSE;
+}
+#endif
+
 void 
 modest_window_mgr_register_window (ModestWindowMgr *self, 
                                   ModestWindow *window)
@@ -625,6 +669,24 @@ modest_window_mgr_register_window (ModestWindowMgr *self,
        g_object_ref (window);
        priv->window_list = g_list_prepend (priv->window_list, window);
 
+       /* Listen to window state changes. Unfortunately
+          window-state-event does not properly work for the Maemo
+          version, so we need to use is-topmost and the ifdef */
+#ifndef MODEST_TOOLKIT_GTK
+       priv->window_state_uids = 
+               modest_signal_mgr_connect (priv->window_state_uids, 
+                                          G_OBJECT (window), 
+                                          "notify::is-topmost",
+                                          G_CALLBACK (on_window_is_topmost), 
+                                          self);
+#else
+       priv->window_state_uids = 
+               modest_signal_mgr_connect (priv->window_state_uids, 
+                                          G_OBJECT (window), 
+                                          "window-state-event",
+                                          G_CALLBACK (on_window_state_event), 
+                                          self);
+#endif
        /* Listen to object destruction */
        handler_id = g_malloc0 (sizeof (gint));
        *handler_id = g_signal_connect (window, "delete-event", G_CALLBACK (on_window_destroy), self);
@@ -649,12 +711,38 @@ modest_window_mgr_register_window (ModestWindowMgr *self,
        modest_window_show_toolbar (window, modest_conf_get_bool (modest_runtime_get_conf (), key, NULL));
 }
 
+static void
+cancel_window_operations (ModestWindow *window)
+{
+       GSList* pending_ops = NULL;
+
+       /* cancel open and receive operations */
+       pending_ops = modest_mail_operation_queue_get_by_source (modest_runtime_get_mail_operation_queue (), 
+                                                                G_OBJECT (window));
+       while (pending_ops != NULL) {
+               ModestMailOperationTypeOperation type;
+               GSList* tmp_list = NULL;
+
+               type = modest_mail_operation_get_type_operation (MODEST_MAIL_OPERATION (pending_ops->data));
+               if (type == MODEST_MAIL_OPERATION_TYPE_RECEIVE || type == MODEST_MAIL_OPERATION_TYPE_OPEN) {
+                       modest_mail_operation_cancel (pending_ops->data);
+               }
+               g_object_unref (G_OBJECT (pending_ops->data));
+               tmp_list = pending_ops;
+               pending_ops = g_slist_next (pending_ops);
+               g_slist_free_1 (tmp_list);
+       }
+}
+
+
+
 static gboolean
 on_window_destroy (ModestWindow *window, 
                   GdkEvent *event,
                   ModestWindowMgr *self)
 {
        gint dialog_response = GTK_RESPONSE_NONE;
+       gboolean no_propagate = FALSE;
 
        /* Specific stuff first */
        if (MODEST_IS_MAIN_WINDOW (window)) {
@@ -665,17 +753,18 @@ on_window_destroy (ModestWindow *window,
                if (g_list_length (priv->window_list) > 1) {
 
                        /* Present the window if it's not visible now */
-                       if (!gtk_window_has_toplevel_focus (GTK_WINDOW (window)))
+                       if (!gtk_window_has_toplevel_focus (GTK_WINDOW (window))) {
                                gtk_window_present (GTK_WINDOW (window));
+                               priv->current_top = window;
+                       }
                        /* Create the confirmation dialog MSG-NOT308 */
                        dialog_response = modest_platform_run_confirmation_dialog (
                                        GTK_WINDOW (window), _("emev_nc_close_windows"));
 
                        /* If the user wants to close all the windows */
                        if ((dialog_response == GTK_RESPONSE_OK) 
-                                       || (dialog_response == GTK_RESPONSE_ACCEPT) 
-                                       || (dialog_response == GTK_RESPONSE_YES))
-                               {
+                           || (dialog_response == GTK_RESPONSE_ACCEPT) 
+                           || (dialog_response == GTK_RESPONSE_YES)) {
                                        GList *iter = priv->window_list;
                                        do {
                                                if (iter->data != window) {
@@ -688,12 +777,22 @@ on_window_destroy (ModestWindow *window,
                                                        iter = g_list_next (iter);
                                                }
                                        } while (iter);
-                               }
-                       else
-                               {
-                                       return TRUE;
-                               }
+                       } else {
+                               return TRUE;
+                       }
                }
+
+               /* Do not unregister it, just hide */
+               gtk_widget_hide_all (GTK_WIDGET (window));
+
+               /* Cancel pending operations */
+               cancel_window_operations (window);
+
+               /* Fake the window system, make it think that there is no window */
+               if (modest_window_mgr_num_windows (self) == 0)
+                       g_signal_emit (self, signals[WINDOW_LIST_EMPTY_SIGNAL], 0);
+
+               no_propagate = TRUE;
        }
        else {
                if (MODEST_IS_MSG_EDIT_WINDOW (window)) {
@@ -711,16 +810,17 @@ on_window_destroy (ModestWindow *window,
                                        modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
                                                                                 _("mcen_nc_no_email_message_modified_save_changes"));
                                /* Save to drafts */
-                               if (response != GTK_RESPONSE_CANCEL)
-                                       modest_ui_actions_on_save_to_drafts (NULL, MODEST_MSG_EDIT_WINDOW (window));                            
+                               if (response == GTK_RESPONSE_OK)
+                                       if (!modest_ui_actions_on_save_to_drafts (NULL, MODEST_MSG_EDIT_WINDOW (window)))
+                                               return TRUE;
                        }
                }
+               /* Unregister window */
+               modest_window_mgr_unregister_window (self, window);
+               no_propagate = TRUE;
        }
 
-       /* Unregister window */
-       modest_window_mgr_unregister_window (self, window);
-       
-       return FALSE;
+       return no_propagate;
 }
 
 static void
@@ -769,7 +869,7 @@ modest_window_mgr_unregister_window (ModestWindowMgr *self,
        }
 
        /* Remove the viewer window handler from the hash table. The
-          HashTable could not exist if the main window was closeed
+          HashTable could not exist if the main window was closed
           when there were other windows remaining */
        if (MODEST_IS_MSG_VIEW_WINDOW (window) && priv->viewer_handlers) {
                tmp = (gulong *) g_hash_table_lookup (priv->viewer_handlers, window);
@@ -789,26 +889,32 @@ modest_window_mgr_unregister_window (ModestWindowMgr *self,
        priv->window_list = g_list_remove_link (priv->window_list, win);
        tmp = g_hash_table_lookup (priv->destroy_handlers, window);
        handler_id = *tmp;
+
        g_hash_table_remove (priv->destroy_handlers, window);
 
        /* cancel open and receive operations */
-       if (MODEST_IS_MSG_VIEW_WINDOW (window)) {
-               ModestMailOperationTypeOperation type;
-               GSList* pending_ops = NULL;
-               GSList* tmp_list = NULL;
-               pending_ops = modest_mail_operation_queue_get_by_source (
-                               modest_runtime_get_mail_operation_queue (), 
-                               G_OBJECT (window));
-               while (pending_ops != NULL) {
-                       type = modest_mail_operation_get_type_operation (MODEST_MAIL_OPERATION (pending_ops->data));
-                       if (type == MODEST_MAIL_OPERATION_TYPE_RECEIVE || type == MODEST_MAIL_OPERATION_TYPE_OPEN) {
-                               modest_mail_operation_cancel (pending_ops->data);
-                       }
-                       g_object_unref (G_OBJECT (pending_ops->data));
-                       tmp_list = pending_ops;
-                       pending_ops = g_slist_next (pending_ops);
-                       g_slist_free_1 (tmp_list);
-               }
+       cancel_window_operations (window);
+
+       /* Check if it's the topmost window, and remove the window from the stack.
+        * This is needed for the cases there's no other topmost window that will
+        * replace it in topmost handler.
+        */
+        if (window == priv->current_top)
+                priv->current_top = NULL;
+
+       /* Disconnect the "window-state-event" handler, we won't need it anymore */
+       if (priv->window_state_uids) {
+#ifndef MODEST_TOOLKIT_GTK
+               priv->window_state_uids = 
+                       modest_signal_mgr_disconnect (priv->window_state_uids, 
+                                                     G_OBJECT (window), 
+                                                     "notify::is-topmost");
+#else
+               priv->window_state_uids = 
+                       modest_signal_mgr_disconnect (priv->window_state_uids, 
+                                                     G_OBJECT (window), 
+                                                     "window-state-event");
+#endif
        }
        
        /* Disconnect the "delete-event" handler, we won't need it anymore */
@@ -818,10 +924,12 @@ modest_window_mgr_unregister_window (ModestWindowMgr *self,
        modest_window_disconnect_signals (window);
        
        /* Destroy the window */
+       g_object_unref (win->data);
        gtk_widget_destroy (win->data);
+       g_list_free (win);
        
        /* If there are no more windows registered emit the signal */
-       if (priv->window_list == NULL && priv->banner_counter == 0)
+       if (modest_window_mgr_num_windows (self) == 0)
                g_signal_emit (self, signals[WINDOW_LIST_EMPTY_SIGNAL], 0);
 }
 
@@ -983,6 +1091,14 @@ modest_window_mgr_set_modal (ModestWindowMgr *self,
        g_mutex_unlock (priv->queue_lock);
 
        if (!old_modal) {       
+               /* make us transient wrt the main window then */
+               if (priv->current_top && ((GtkWindow *) priv->current_top != window)) {
+                       gtk_window_set_transient_for (window, GTK_WINDOW(priv->current_top));
+               } else {
+                       ModestWindow *main_win = modest_window_mgr_get_main_window (self, FALSE);
+                       if (GTK_WINDOW(main_win) != window) /* they should not be the same */
+                               gtk_window_set_transient_for (window, GTK_WINDOW(main_win));
+               }
                gtk_window_set_modal (window, TRUE);
        } else {
                /* un-modalize the old one; the one on top should be the
@@ -996,7 +1112,7 @@ modest_window_mgr_set_modal (ModestWindowMgr *self,
        g_queue_push_head (priv->modal_windows, window);
        g_mutex_unlock (priv->queue_lock);
 
-       if (GTK_IS_DIALOG (window))
+       if (GTK_IS_DIALOG (window)) {
                /* Note that response is not always enough because it
                   could be captured and removed easily by dialogs but
                   works for most of situations */
@@ -1006,13 +1122,25 @@ modest_window_mgr_set_modal (ModestWindowMgr *self,
                                                   "response",
                                                   G_CALLBACK (on_modal_dialog_close), 
                                                   self);
-       else
+               /* We need this as well because dialogs are often
+                  destroyed with gtk_widget_destroy and this one will
+                  prevent response from happening */
+               priv->modal_handler_uids = 
+                       modest_signal_mgr_connect (priv->modal_handler_uids, 
+                                                  G_OBJECT (window), 
+                                                  "destroy",
+                                                  G_CALLBACK (on_modal_dialog_destroy),
+                                                  self);
+       } else {
                priv->modal_handler_uids = 
                        modest_signal_mgr_connect (priv->modal_handler_uids, 
                                                   G_OBJECT (window), 
                                                   "delete-event",
                                                   G_CALLBACK (on_modal_window_close), 
                                                   self);
+       }
+       /* Destroy width parent */
+       gtk_window_set_destroy_with_parent (window, TRUE);
 }
 
 
@@ -1106,8 +1234,15 @@ idle_top_modal (gpointer data)
        g_mutex_unlock (priv->queue_lock);
 
        /* Show it */
-       if (topmost)
+       if (topmost) {
+               gdk_threads_enter ();
                gtk_window_present (topmost);
+               /* It seems that the window looses modality if some
+                  other is shown on top of it after set_transient_for
+                  and set_parent */
+               gtk_window_set_modal (topmost, TRUE);
+               gdk_threads_leave ();
+       }
 
        return FALSE;
 }
@@ -1134,12 +1269,17 @@ remove_modal_from_queue (GtkWidget *widget,
        g_mutex_unlock (priv->queue_lock);
 
        /* Disconnect handler */
-       priv->modal_handler_uids = 
-               modest_signal_mgr_disconnect (priv->modal_handler_uids, 
+       priv->modal_handler_uids =
+               modest_signal_mgr_disconnect (priv->modal_handler_uids,
                                              G_OBJECT (widget),
-                                             GTK_IS_DIALOG (widget) ? 
-                                             "response" : 
-                                             "destroy-event");
+                                             GTK_IS_DIALOG (widget) ?
+                                             "response" :
+                                             "delete-event");
+       if (GTK_IS_DIALOG (widget))
+               priv->modal_handler_uids =
+                       modest_signal_mgr_disconnect (priv->modal_handler_uids,
+                                                     G_OBJECT (widget),
+                                                     "destroy");
 
        /* Schedule the next one for being shown */
        g_idle_add (idle_top_modal, self);
@@ -1166,10 +1306,22 @@ on_modal_dialog_close (GtkDialog *dialog,
 {
        ModestWindowMgr *self = MODEST_WINDOW_MGR (user_data);
 
-       /* Remove modal window from queue */
+       /* Remove modal window from queue. Note that if "destroy"
+          signal was invoked before the response the window could be
+          already deleted */
        remove_modal_from_queue (GTK_WIDGET (dialog), self);
 }
 
+static void
+on_modal_dialog_destroy (GtkObject *object,
+                        gpointer   user_data)
+{
+       ModestWindowMgr *self = MODEST_WINDOW_MGR (user_data);
+
+       /* Remove modal window from queue */
+       remove_modal_from_queue (GTK_WIDGET (object), self);
+}
+
 gint 
 modest_window_mgr_num_windows (ModestWindowMgr *self)
 {
@@ -1183,6 +1335,10 @@ modest_window_mgr_num_windows (ModestWindowMgr *self)
        if (priv->window_list)
                num_windows = g_list_length (priv->window_list);
 
+       /* Do not take into account the main window if it was hidden */
+       if (priv->main_window && !GTK_WIDGET_VISIBLE (priv->main_window))
+               num_windows--;
+
        return num_windows + priv->banner_counter;
 }
 
@@ -1249,8 +1405,6 @@ modest_window_mgr_unregister_banner (ModestWindowMgr *self)
        priv = MODEST_WINDOW_MGR_GET_PRIVATE (self);
 
        priv->banner_counter--;
-       if (priv->window_list == NULL && priv->banner_counter == 0) {
+       if (modest_window_mgr_num_windows (self) == 0)
                g_signal_emit (self, signals[WINDOW_LIST_EMPTY_SIGNAL], 0);
-       }
-
 }