[frogr] Convert FrogrMainView in a subclass of GtkApplicationWindow



commit 1cb07b8328e949b9b4a7a8474b0604c4b53f872d
Author: Mario Sanchez Prada <msanchez gnome org>
Date:   Sat Nov 17 03:06:03 2012 +0100

    Convert FrogrMainView in a subclass of GtkApplicationWindow

 src/frogr-controller.c  |   81 +-
 src/frogr-file-loader.c |    4 +-
 src/frogr-main-view.c   | 3000 +++++++++++++++++++++++------------------------
 src/frogr-main-view.h   |    6 +-
 4 files changed, 1488 insertions(+), 1603 deletions(-)
---
diff --git a/src/frogr-controller.c b/src/frogr-controller.c
index 835f5f8..79d1f37 100644
--- a/src/frogr-controller.c
+++ b/src/frogr-controller.c
@@ -305,19 +305,19 @@ _g_application_startup_cb (GApplication *app, gpointer data)
 static void
 _g_application_activate_cb (GApplication *app, gpointer data)
 {
-  FrogrController *self = FROGR_CONTROLLER (data);
-  FrogrControllerPrivate *priv = FROGR_CONTROLLER_GET_PRIVATE (self);
-  GtkWindow *mainview_window = NULL;
+  FrogrControllerPrivate *priv = FROGR_CONTROLLER_GET_PRIVATE (data);
 
   DEBUG ("%s", "Application activated!\n");
 
-  mainview_window = frogr_main_view_get_window (priv->mainview);
-  gtk_window_present (mainview_window);
+  /* Show the UI */
+  gtk_widget_show_all (GTK_WIDGET(priv->mainview));
+  gtk_window_present (GTK_WINDOW (priv->mainview));
 }
 
 static void
 _g_application_open_files_cb (GApplication *app, GFile **files, gint n_files, gchar *hint, gpointer data)
 {
+  FrogrControllerPrivate *priv = FROGR_CONTROLLER_GET_PRIVATE (data);
   GSList *fileuris = NULL;
   int i = 0;
 
@@ -334,13 +334,16 @@ _g_application_open_files_cb (GApplication *app, GFile **files, gint n_files, gc
 
   if (fileuris)
     gdk_threads_add_idle (_load_pictures_on_idle, fileuris);
+
+  /* Show the UI */
+  gtk_widget_show_all (GTK_WIDGET(priv->mainview));
+  gtk_window_present (GTK_WINDOW (priv->mainview));
 }
 
 static void
 _g_application_shutdown_cb (GApplication *app, gpointer data)
 {
-  FrogrController *self = FROGR_CONTROLLER (data);
-  FrogrControllerPrivate *priv = FROGR_CONTROLLER_GET_PRIVATE (self);
+  FrogrControllerPrivate *priv = FROGR_CONTROLLER_GET_PRIVATE (data);
 
   DEBUG ("%s", "Shutting down application...");
 
@@ -349,8 +352,7 @@ _g_application_shutdown_cb (GApplication *app, gpointer data)
       while (gtk_events_pending ())
         gtk_main_iteration ();
 
-      g_object_unref (priv->mainview);
-
+      gtk_widget_destroy (GTK_WIDGET (priv->mainview));
       priv->app_running = FALSE;
 
       frogr_config_save_all (priv->config);
@@ -504,11 +506,7 @@ _handle_flicksoup_error (FrogrController *self, GError *error, gboolean notify_u
     }
 
   if (notify_user && error_function)
-    {
-      GtkWindow *window = NULL;
-      window = frogr_main_view_get_window (priv->mainview);
-      error_function (window, msg);
-    }
+    error_function (GTK_WINDOW (priv->mainview), msg);
 
   DEBUG ("%s", msg);
   g_free (msg);
@@ -575,15 +573,13 @@ _get_auth_url_cb (GObject *obj, GAsyncResult *res, gpointer data)
   auth_url = fsp_session_get_auth_url_finish (priv->session, res, &error);
   if (auth_url != NULL && error == NULL)
     {
-      GtkWindow *window = NULL;
       gchar *url_with_permissions = NULL;
 
       url_with_permissions = g_strdup_printf ("%s&perms=write", auth_url);
       frogr_util_open_uri (url_with_permissions);
 
       /* Run the auth confirmation dialog */
-      window = frogr_main_view_get_window (priv->mainview);
-      frogr_auth_dialog_show (window, CONFIRM_AUTHORIZATION);
+      frogr_auth_dialog_show (GTK_WINDOW (priv->mainview), CONFIRM_AUTHORIZATION);
 
       DEBUG ("Auth URL: %s", url_with_permissions);
 
@@ -702,13 +698,10 @@ _cancel_authorization_on_timeout (gpointer data)
 
   if (priv->fetching_auth_url || priv->fetching_auth_token || priv->fetching_token_replacement)
     {
-      GtkWindow *window = NULL;
-
       frogr_controller_cancel_ongoing_requests (self);
       frogr_main_view_hide_progress (priv->mainview);
 
-      window = frogr_main_view_get_window (priv->mainview);
-      _show_auth_failed_dialog (window, _("Authorization failed (timed out)"), FALSE);
+      _show_auth_failed_dialog (GTK_WINDOW (priv->mainview), _("Authorization failed (timed out)"), FALSE);
     }
 
   return FALSE;
@@ -1925,7 +1918,6 @@ _show_details_dialog_on_idle (GSList *pictures)
   FrogrControllerPrivate *priv = NULL;
   FrogrMainView *mainview = NULL;
   FrogrModel *model = NULL;
-  GtkWindow *window = NULL;
   GSList *tags_list = NULL;
 
   controller = frogr_controller_get_instance ();
@@ -1942,8 +1934,7 @@ _show_details_dialog_on_idle (GSList *pictures)
   tags_list = frogr_model_get_tags (model);
 
   /* Sets already pre-fetched: show the dialog */
-  window = frogr_main_view_get_window (priv->mainview);
-  frogr_details_dialog_show (window, pictures, tags_list);
+  frogr_details_dialog_show (GTK_WINDOW (priv->mainview), pictures, tags_list);
 
   /* FrogrController's responsibility over this list ends here */
   g_slist_foreach (pictures, (GFunc) g_object_unref, NULL);
@@ -1959,7 +1950,6 @@ _show_add_tags_dialog_on_idle (GSList *pictures)
   FrogrControllerPrivate *priv = NULL;
   FrogrMainView *mainview = NULL;
   FrogrModel *model = NULL;
-  GtkWindow *window = NULL;
   GSList *tags_list = NULL;
 
   controller = frogr_controller_get_instance ();
@@ -1976,8 +1966,7 @@ _show_add_tags_dialog_on_idle (GSList *pictures)
   tags_list = frogr_model_get_tags (model);
 
   /* Sets already pre-fetched: show the dialog */
-  window = frogr_main_view_get_window (priv->mainview);
-  frogr_add_tags_dialog_show (window, pictures, tags_list);
+  frogr_add_tags_dialog_show (GTK_WINDOW (priv->mainview), pictures, tags_list);
 
   /* FrogrController's responsibility over this list ends here */
   g_slist_foreach (pictures, (GFunc) g_object_unref, NULL);
@@ -1993,7 +1982,6 @@ _show_create_new_set_dialog_on_idle (GSList *pictures)
   FrogrControllerPrivate *priv = NULL;
   FrogrMainView *mainview = NULL;
   FrogrModel *model = NULL;
-  GtkWindow *window = NULL;
   GSList *photosets = NULL;
 
   controller = frogr_controller_get_instance ();
@@ -2009,8 +1997,7 @@ _show_create_new_set_dialog_on_idle (GSList *pictures)
   model = frogr_main_view_get_model (priv->mainview);
   photosets = frogr_model_get_photosets (model);
 
-  window = frogr_main_view_get_window (priv->mainview);
-  frogr_create_new_set_dialog_show (window, pictures, photosets);
+  frogr_create_new_set_dialog_show (GTK_WINDOW (priv->mainview), pictures, photosets);
 
   /* FrogrController's responsibility over this list ends here */
   g_slist_foreach (pictures, (GFunc) g_object_unref, NULL);
@@ -2026,7 +2013,6 @@ _show_add_to_set_dialog_on_idle (GSList *pictures)
   FrogrControllerPrivate *priv = NULL;
   FrogrMainView *mainview = NULL;
   FrogrModel *model = NULL;
-  GtkWindow *window = NULL;
   GSList *photosets = NULL;
 
   controller = frogr_controller_get_instance ();
@@ -2042,11 +2028,10 @@ _show_add_to_set_dialog_on_idle (GSList *pictures)
   model = frogr_main_view_get_model (priv->mainview);
   photosets = frogr_model_get_photosets (model);
 
-  window = frogr_main_view_get_window (priv->mainview);
   if (frogr_model_n_photosets (model) > 0)
-    frogr_add_to_set_dialog_show (window, pictures, photosets);
+    frogr_add_to_set_dialog_show (GTK_WINDOW (priv->mainview), pictures, photosets);
   else if (priv->photosets_fetched)
-    frogr_util_show_info_dialog (window, _("No sets found"));
+    frogr_util_show_info_dialog (GTK_WINDOW (priv->mainview), _("No sets found"));
 
   /* FrogrController's responsibility over this list ends here */
   g_slist_foreach (pictures, (GFunc) g_object_unref, NULL);
@@ -2062,7 +2047,6 @@ _show_add_to_group_dialog_on_idle (GSList *pictures)
   FrogrControllerPrivate *priv = NULL;
   FrogrMainView *mainview = NULL;
   FrogrModel *model = NULL;
-  GtkWindow *window = NULL;
   GSList *groups = NULL;
 
   controller = frogr_controller_get_instance ();
@@ -2078,11 +2062,10 @@ _show_add_to_group_dialog_on_idle (GSList *pictures)
   model = frogr_main_view_get_model (priv->mainview);
   groups = frogr_model_get_groups (model);
 
-  window = frogr_main_view_get_window (priv->mainview);
   if (frogr_model_n_groups (model) > 0)
-    frogr_add_to_group_dialog_show (window, pictures, groups);
+    frogr_add_to_group_dialog_show (GTK_WINDOW (priv->mainview), pictures, groups);
   else if (priv->groups_fetched)
-    frogr_util_show_info_dialog (window, _("No groups found"));
+    frogr_util_show_info_dialog (GTK_WINDOW (priv->mainview), _("No groups found"));
 
   /* FrogrController's responsibility over this list ends here */
   g_slist_foreach (pictures, (GFunc) g_object_unref, NULL);
@@ -2492,22 +2475,18 @@ void
 frogr_controller_show_about_dialog (FrogrController *self)
 {
   FrogrControllerPrivate *priv = NULL;
-  GtkWindow *window = NULL;
 
   g_return_if_fail(FROGR_IS_CONTROLLER (self));
 
-  priv = FROGR_CONTROLLER_GET_PRIVATE (self);
-  window = frogr_main_view_get_window (priv->mainview);
-
   /* Run the about dialog */
-  frogr_about_dialog_show (window);
+  priv = FROGR_CONTROLLER_GET_PRIVATE (self);
+  frogr_about_dialog_show (GTK_WINDOW (priv->mainview));
 }
 
 void
 frogr_controller_show_auth_dialog (FrogrController *self)
 {
   FrogrControllerPrivate *priv = NULL;
-  GtkWindow *window = NULL;
 
   g_return_if_fail(FROGR_IS_CONTROLLER (self));
 
@@ -2519,23 +2498,19 @@ frogr_controller_show_auth_dialog (FrogrController *self)
     return;
 
   /* Run the auth dialog */
-  window = frogr_main_view_get_window (priv->mainview);
-  frogr_auth_dialog_show (window, REQUEST_AUTHORIZATION);
+  frogr_auth_dialog_show (GTK_WINDOW (priv->mainview), REQUEST_AUTHORIZATION);
 }
 
 void
 frogr_controller_show_settings_dialog (FrogrController *self)
 {
   FrogrControllerPrivate *priv = NULL;
-  GtkWindow *window = NULL;
 
   g_return_if_fail(FROGR_IS_CONTROLLER (self));
 
-  priv = FROGR_CONTROLLER_GET_PRIVATE (self);
-  window = frogr_main_view_get_window (priv->mainview);
-
   /* Run the auth dialog */
-  frogr_settings_dialog_show (window);
+  priv = FROGR_CONTROLLER_GET_PRIVATE (self);
+  frogr_settings_dialog_show (GTK_WINDOW (priv->mainview));
 }
 
 void
@@ -2765,15 +2740,13 @@ frogr_controller_upload_pictures (FrogrController *self)
   /* Upload pictures */
   if (!frogr_controller_is_authorized (self))
     {
-      GtkWindow *window = NULL;
       gchar *msg = NULL;
 
       msg = g_strdup_printf (_("You need to properly authorize %s before"
                                " uploading any pictures to Flickr.\n"
                                "Please re-authorize it."), APP_SHORTNAME);
 
-      window = frogr_main_view_get_window (priv->mainview);
-      frogr_util_show_error_dialog (window, msg);
+      frogr_util_show_error_dialog (GTK_WINDOW (priv->mainview), msg);
       g_free (msg);
     }
   else
diff --git a/src/frogr-file-loader.c b/src/frogr-file-loader.c
index a5e2a2e..dd9a061 100644
--- a/src/frogr-file-loader.c
+++ b/src/frogr-file-loader.c
@@ -757,7 +757,6 @@ _check_filesize_limits (FrogrFileLoader *self, FrogrPicture *picture)
 
   if (picture_filesize > max_filesize)
     {
-      GtkWindow *window = NULL;
       gchar *msg = NULL;
 
       /* First %s is the title of the picture (filename of the file by
@@ -768,8 +767,7 @@ _check_filesize_limits (FrogrFileLoader *self, FrogrPicture *picture)
                              frogr_picture_get_title (picture),
                              frogr_util_get_datasize_string (max_filesize));
 
-      window = frogr_main_view_get_window (priv->mainview);
-      frogr_util_show_error_dialog (window, msg);
+      frogr_util_show_error_dialog (GTK_WINDOW (priv->mainview), msg);
       g_free (msg);
 
       keep_going = FALSE;
diff --git a/src/frogr-main-view.c b/src/frogr-main-view.c
index 4c92ef6..fda8bc7 100644
--- a/src/frogr-main-view.c
+++ b/src/frogr-main-view.c
@@ -58,7 +58,7 @@
                                 FROGR_TYPE_MAIN_VIEW,   \
                                 FrogrMainViewPrivate))
 
-G_DEFINE_TYPE (FrogrMainView, frogr_main_view, G_TYPE_OBJECT)
+G_DEFINE_TYPE (FrogrMainView, frogr_main_view, GTK_TYPE_APPLICATION_WINDOW)
 
 /* Private Data */
 
@@ -74,8 +74,6 @@ typedef struct _FrogrMainViewPrivate {
   gboolean tooltips_enabled;
   gint n_selected_pictures;
 
-  GtkApplication *gtk_app;
-  GtkWindow *window;
   gchar *project_name;
   gchar *project_dir;
   gchar *project_filepath;
@@ -124,12 +122,6 @@ typedef struct _FrogrMainViewPrivate {
 } FrogrMainViewPrivate;
 
 
-/* Properties */
-enum  {
-  PROP_0,
-  PROP_GTK_APPLICATION
-};
-
 /* Icon view treeview */
 enum {
   FILEURI_COL,
@@ -139,6 +131,8 @@ enum {
 
 /* Prototypes */
 
+static void _initialize_ui (FrogrMainView *self);
+static gboolean _maybe_show_auth_dialog_on_idle (FrogrMainView *self);
 static void _load_project_action (GSimpleAction *action, GVariant *parameter, gpointer data);
 static void _save_project_action (GSimpleAction *action, GVariant *parameter, gpointer data);
 static void _save_project_as_action (GSimpleAction *action, GVariant *parameter, gpointer data);
@@ -151,8 +145,6 @@ static void _quit_action (GSimpleAction *action, GVariant *parameter, gpointer d
 static void _update_project_path (FrogrMainView *self, const gchar *path);
 static void _update_window_title (FrogrMainView *self, gboolean dirty);
 
-static gboolean _maybe_show_auth_dialog_on_idle (FrogrMainView *self);
-
 #ifdef MAC_INTEGRATION
 static gboolean osx_can_activate_cb(GtkWidget* widget, guint signal_id, gpointer data);
 static void _tweak_menu_bar_for_mac (FrogrMainView *self);
@@ -278,1914 +270,1889 @@ static void _update_ui (FrogrMainView *self);
 
 /* Private API */
 
-static void
-_load_project_action (GSimpleAction *action,
-                      GVariant *parameter,
-                      gpointer data)
-{
-  _load_project_dialog (FROGR_MAIN_VIEW (data));
-}
-
-static void
-_save_project_action (GSimpleAction *action,
-                       GVariant *parameter,
-                       gpointer data)
-{
-  _save_current_project (FROGR_MAIN_VIEW (data));
-}
-
-static void
-_save_project_as_action (GSimpleAction *action,
-                         GVariant *parameter,
-                         gpointer data)
-{
-  _save_project_as_dialog (FROGR_MAIN_VIEW (data));
-}
-
-static void
-_authorize_action (GSimpleAction *action,
-                   GVariant *parameter,
-                   gpointer data)
-{
-  FrogrMainViewPrivate *priv = NULL;
-  priv = FROGR_MAIN_VIEW_GET_PRIVATE (data);
-  frogr_controller_show_auth_dialog (priv->controller);
-}
-
-static void
-_preferences_action (GSimpleAction *action,
-                     GVariant *parameter,
-                     gpointer data)
-{
-  FrogrMainViewPrivate *priv = NULL;
-  priv = FROGR_MAIN_VIEW_GET_PRIVATE (data);
-  frogr_controller_show_settings_dialog (priv->controller);
-}
+static GActionEntry app_entries[] = {
+  { "load_project", _load_project_action, NULL, NULL, NULL },
+  { "save_project", _save_project_action, NULL, NULL, NULL },
+  { "save_project_as", _save_project_as_action, NULL, NULL, NULL },
+  { "authorize", _authorize_action, NULL, NULL, NULL },
+  { "preferences", _preferences_action, NULL, NULL, NULL },
+  { "help", _help_action, NULL, NULL, NULL },
+  { "about", _about_action, NULL, NULL, NULL },
+  { "quit", _quit_action, NULL, NULL, NULL },
+};
 
 static void
-_about_action (GSimpleAction *action,
-               GVariant *parameter,
-               gpointer data)
+_initialize_ui (FrogrMainView *self)
 {
   FrogrMainViewPrivate *priv = NULL;
-  priv = FROGR_MAIN_VIEW_GET_PRIVATE (data);
-  frogr_controller_show_about_dialog (priv->controller);
-}
-
-static void
-_help_action (GSimpleAction *action,
-              GVariant *parameter,
-              gpointer data)
-{
-  frogr_util_open_uri ("ghelp:frogr");
-}
-
-static void
-_quit_action (GSimpleAction *action,
-              GVariant *parameter,
-              gpointer data)
-{
-  _quit_application (FROGR_MAIN_VIEW (data));
-}
-
-static void
-_update_project_path (FrogrMainView *self, const gchar *path)
-{
-  FrogrMainViewPrivate *priv = FROGR_MAIN_VIEW_GET_PRIVATE (self);
-
-  /* Early return if nothing changed */
-  if (!g_strcmp0 (priv->project_filepath, path))
-      return;
-
-  g_free (priv->project_name);
-  g_free (priv->project_dir);
-  g_free (priv->project_filepath);
-
-  if (!path)
-    {
-      priv->project_name = NULL;
-      priv->project_dir = NULL;
-      priv->project_filepath = NULL;
-    }
-  else
-    {
-      GFile *file = NULL;
-      GFile *dir = NULL;
-      GFileInfo *file_info = NULL;
-      gchar *dir_path = NULL;
-      const gchar *home_dir = NULL;
-
-      /* Get the display name in beautiful UTF-8 */
-      file = g_file_new_for_path (path);
-      file_info = g_file_query_info (file,
-                                     G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME,
-                                     G_FILE_QUERY_INFO_NONE,
-                                     NULL,
-                                     NULL);
-      priv->project_name = g_strdup (g_file_info_get_display_name (file_info));
-
-      /* Get the base directory, in beautiful UTF-8 too */
-      dir = g_file_get_parent (file);
-      dir_path = g_file_get_parse_name (dir);
-      home_dir = g_get_home_dir ();
-      if (g_str_has_prefix (dir_path, home_dir))
-        {
-          gchar *tmp_path = NULL;
+  GtkApplication *gtk_app;
+  GtkBuilder *builder;
+  GtkWidget *main_vbox;
+  GtkWidget *icon_view;
+  GtkWidget *status_bar;
+  GtkWidget *progress_dialog;
+  GtkWidget *progress_vbox;
+  GtkWidget *progress_bar;
+  GtkWidget *progress_label;
+  GtkWidget *toolbar;
+  const gchar *icons_path = NULL;
+  gchar *full_path = NULL;
+  GList *icons = NULL;
 
-          tmp_path = dir_path;
-          dir_path = g_strdup_printf ("~%s", &dir_path[g_utf8_strlen (home_dir, -1)]);
-          g_free (tmp_path);
-        }
-      priv->project_dir = dir_path;
+  priv = FROGR_MAIN_VIEW_GET_PRIVATE (self);
 
-      /* Finally, store the raw path too */
-      priv->project_filepath = g_strdup (path);
+  /* Provide a default icon list in several sizes */
+  icons_path = frogr_util_get_icons_dir ();
+  full_path = g_strdup_printf ("%s/" MAIN_VIEW_ICON("128x128"), icons_path);
+  icons = g_list_prepend (icons, gdk_pixbuf_new_from_file (full_path, NULL));
+  g_free (full_path);
 
-      g_object_unref (file);
-      g_object_unref (dir);
-    }
-}
+  full_path = g_strdup_printf ("%s/" MAIN_VIEW_ICON("64x64"), icons_path);
+  icons = g_list_prepend (icons, gdk_pixbuf_new_from_file (full_path, NULL));
+  g_free (full_path);
 
-static void
-_update_window_title (FrogrMainView *self, gboolean dirty)
-{
-  FrogrMainViewPrivate *priv = FROGR_MAIN_VIEW_GET_PRIVATE (self);
+  full_path = g_strdup_printf ("%s/" MAIN_VIEW_ICON("48x48"), icons_path);
+  icons = g_list_prepend (icons, gdk_pixbuf_new_from_file (full_path, NULL));
+  g_free (full_path);
 
-  gchar *session_string = NULL;
-  gchar *window_title = NULL;
+  full_path = g_strdup_printf ("%s/" MAIN_VIEW_ICON("32x32"), icons_path);
+  icons = g_list_prepend (icons, gdk_pixbuf_new_from_file (full_path, NULL));
+  g_free (full_path);
 
-  session_string = priv->project_name
-    ? g_strdup_printf ("%s%s (%s) - ", (dirty ? "*" : ""),
-                       priv->project_name, priv->project_dir)
-    : g_strdup("");
+  full_path = g_strdup_printf ("%s/" MAIN_VIEW_ICON("24x24"), icons_path);
+  icons = g_list_prepend (icons, gdk_pixbuf_new_from_file (full_path, NULL));
+  g_free (full_path);
 
-  window_title = g_strdup_printf ("%s%s", session_string, APP_SHORTNAME);
-  g_free (session_string);
+  full_path = g_strdup_printf ("%s/" MAIN_VIEW_ICON("16x16"), icons_path);
+  icons = g_list_prepend (icons, gdk_pixbuf_new_from_file (full_path, NULL));
+  g_free (full_path);
 
-  gtk_window_set_title (GTK_WINDOW (priv->window), window_title);
-  g_free (window_title);
-}
+  gtk_window_set_default_icon_list (icons);
+  g_list_foreach (icons, (GFunc) g_object_unref, NULL);
+  g_list_free (icons);
 
-static gboolean
-_maybe_show_auth_dialog_on_idle (FrogrMainView *self)
-{
-  FrogrMainViewPrivate *priv = FROGR_MAIN_VIEW_GET_PRIVATE (self);
+  /* Get widgets from GtkBuilder */
+  builder = gtk_builder_new ();
+  priv->builder = builder;
 
-  if (!frogr_controller_is_authorized (priv->controller))
-    {
-      /* Show authorization dialog if needed */
-      frogr_controller_show_auth_dialog (priv->controller);
-    }
+  full_path = g_strdup_printf ("%s/" UI_MAIN_VIEW_FILE, frogr_util_get_app_data_dir ());
+  gtk_builder_add_from_file (builder, full_path, NULL);
+  g_free (full_path);
 
-  return FALSE;
-}
+  main_vbox = GTK_WIDGET (gtk_builder_get_object (builder, "main_window_vbox"));
+  gtk_container_add (GTK_CONTAINER (self), main_vbox);
 
-#ifdef MAC_INTEGRATION
-static gboolean
-osx_can_activate_cb(GtkWidget* widget, guint signal_id, gpointer data)
-{
-  return gtk_widget_is_sensitive(widget);
-}
+  /* Menu bar */
+  priv->menu_bar = GTK_WIDGET (gtk_builder_get_object (builder, "menu_bar"));
+  gtk_widget_show_all (priv->menu_bar);
 
-static void
-_tweak_menu_bar_for_mac (FrogrMainView *self)
-{
-  FrogrMainViewPrivate *priv = NULL;
-  GtkOSXApplication *osx_app = NULL;
-  GtkWidget *menu_item = NULL;
+#ifndef MAC_INTEGRATION
+  gtk_box_pack_start (GTK_BOX (main_vbox), priv->menu_bar, FALSE, FALSE, 0);
+  gtk_box_reorder_child (GTK_BOX (main_vbox), priv->menu_bar, 0);
+#endif
 
-  priv = FROGR_MAIN_VIEW_GET_PRIVATE (self);
+  /* App menu */
+  full_path = g_strdup_printf ("%s/" UI_APP_MENU_FILE, frogr_util_get_app_data_dir ());
+  gtk_builder_add_from_file (builder, full_path, NULL);
+  g_free (full_path);
 
-  osx_app = g_object_new (GTK_TYPE_OSX_APPLICATION, NULL);
-  gtk_osxapplication_set_menu_bar (osx_app, GTK_MENU_SHELL(priv->menu_bar));
+  gtk_app = gtk_window_get_application (GTK_WINDOW (self));
+  g_action_map_add_action_entries (G_ACTION_MAP (gtk_app),
+                                   app_entries, G_N_ELEMENTS (app_entries),
+                                   self);
+  gtk_application_set_app_menu (GTK_APPLICATION (gtk_app),
+                                G_MENU_MODEL (gtk_builder_get_object (builder, "app-menu")));
 
-  /* Relocate the 'about' menu item in the app menu */
-  menu_item = GTK_WIDGET (gtk_builder_get_object (priv->builder, "about_menu_item"));
-  gtk_osxapplication_insert_app_menu_item (osx_app, menu_item, 0);
-  gtk_osxapplication_insert_app_menu_item (osx_app, gtk_separator_menu_item_new (), 1);
-  gtk_widget_show_all (menu_item);
+  toolbar = GTK_WIDGET (gtk_builder_get_object (builder, "toolbar"));
+  gtk_style_context_add_class (gtk_widget_get_style_context (toolbar),
+                               GTK_STYLE_CLASS_PRIMARY_TOOLBAR);
 
-  /* Relocate the 'authorize' menu item in the app menu */
-  menu_item = GTK_WIDGET (gtk_builder_get_object (priv->builder, "authorize_menu_item"));
-  gtk_osxapplication_insert_app_menu_item (osx_app, menu_item, 2);
-  gtk_widget_show_all (menu_item);
+  icon_view = GTK_WIDGET (gtk_builder_get_object (builder, "icon_view"));
+  priv->icon_view = icon_view;
 
-  /* Relocate the 'accounts' menu item in the app menu */
-  menu_item = GTK_WIDGET (gtk_builder_get_object (priv->builder, "accounts_menu_item"));
-  gtk_osxapplication_insert_app_menu_item (osx_app, menu_item, 3);
-  gtk_osxapplication_insert_app_menu_item (osx_app, gtk_separator_menu_item_new (), 4);
-  gtk_widget_show_all (menu_item);
+  status_bar = GTK_WIDGET (gtk_builder_get_object (builder, "status_bar"));
+  priv->status_bar = status_bar;
 
-  /* Relocate the 'preferences' menu item in the app menu */
-  menu_item = GTK_WIDGET (gtk_builder_get_object (priv->builder, "preferences_menu_item"));
-  gtk_osxapplication_insert_app_menu_item (osx_app, menu_item, 5);
-  gtk_osxapplication_insert_app_menu_item (osx_app, gtk_separator_menu_item_new (), 6);
-  gtk_widget_show_all (menu_item);
+  /* Get actions from GtkBuilder */
+  priv->load_project_action =
+    GTK_ACTION (gtk_builder_get_object (builder, "load_project_action"));
+  priv->save_project_action =
+    GTK_ACTION (gtk_builder_get_object (builder, "save_project_action"));
+  priv->save_project_as_action =
+    GTK_ACTION (gtk_builder_get_object (builder, "save_project_as_action"));
+  priv->load_pictures_action =
+    GTK_ACTION (gtk_builder_get_object (builder, "load_pictures_action"));
+  priv->remove_pictures_action =
+    GTK_ACTION (gtk_builder_get_object (builder, "remove_pictures_action"));
+  priv->upload_pictures_action =
+    GTK_ACTION (gtk_builder_get_object (builder, "upload_pictures_action"));
+  priv->open_in_external_viewer_action =
+    GTK_ACTION (gtk_builder_get_object (builder,
+                                        "open_in_external_viewer_action"));
+  priv->auth_action =
+    GTK_ACTION (gtk_builder_get_object (builder, "auth_action"));
+  priv->preferences_action =
+    GTK_ACTION (gtk_builder_get_object (builder, "preferences_action"));
+  priv->add_tags_action =
+    GTK_ACTION (gtk_builder_get_object (builder, "add_tags_action"));
+  priv->edit_details_action =
+    GTK_ACTION (gtk_builder_get_object (builder, "edit_details_action"));
+  priv->add_to_group_action =
+    GTK_ACTION (gtk_builder_get_object (builder, "add_to_group_action"));
+  priv->add_to_set_action =
+    GTK_ACTION (gtk_builder_get_object (builder, "add_to_set_action"));
+  priv->add_to_new_set_action =
+    GTK_ACTION (gtk_builder_get_object (builder, "add_to_new_set_action"));
+  priv->help_action =
+    GTK_ACTION (gtk_builder_get_object (builder, "help_action"));
+  priv->about_action =
+    GTK_ACTION (gtk_builder_get_object (builder, "about_action"));
+  priv->enable_tooltips_action =
+    GTK_TOGGLE_ACTION (gtk_builder_get_object (builder,
+                                               "enable_tooltips_action"));
+  priv->sort_by_title_action =
+    GTK_TOGGLE_ACTION (gtk_builder_get_object (builder,
+                                               "sort_by_title_action"));
+  priv->sort_by_date_taken_action =
+    GTK_TOGGLE_ACTION (gtk_builder_get_object (builder,
+                                               "sort_by_date_taken_action"));
+  priv->sort_as_loaded_action =
+    GTK_TOGGLE_ACTION (gtk_builder_get_object (builder,
+                                               "sort_as_loaded_action"));
+  priv->reversed_order_action =
+    GTK_TOGGLE_ACTION (gtk_builder_get_object (builder,
+                                               "reversed_order_action"));
+  /* Set Keyboard shortcuts */
+  _setup_keyboard_shortcuts (self);
 
-  /* Hide the traditional application menu that won't be shown in the Mac */
-  menu_item = GTK_WIDGET (gtk_builder_get_object (priv->builder, "frogr_menu_item"));
-  gtk_widget_hide (menu_item);
+  /* Init main model's state description */
+  _update_state_description (self);
 
-  /* Make sure accelerators are visible when needed */
-  g_signal_connect(priv->menu_bar, "can-activate-accel",
-                   G_CALLBACK(osx_can_activate_cb), NULL);
+  /* Init the details of the current project */
+  _update_project_path (self, NULL);
 
-  gtk_osxapplication_ready(osx_app);
-}
-#endif
+  /* Initialize sorting criteria and reverse in the UI */
+  if (priv->sorting_criteria == SORT_BY_TITLE)
+    gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (priv->sort_by_title_action), TRUE);
+  else if (priv->sorting_criteria == SORT_BY_DATE)
+    gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (priv->sort_by_date_taken_action), TRUE);
+  else
+    gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (priv->sort_as_loaded_action), TRUE);
 
-static void
-_setup_keyboard_shortcuts (FrogrMainView *self)
-{
-  FrogrMainViewPrivate *priv = NULL;
-  GtkAccelGroup *accel = NULL;
-  GtkWidget *menu_item = NULL;
+  gtk_toggle_action_set_active (priv->reversed_order_action, priv->sorting_reversed);
 
-  priv = FROGR_MAIN_VIEW_GET_PRIVATE (self);
+  /* Initialize 'tooltips enabled' in the UI */
+  gtk_toggle_action_set_active (priv->enable_tooltips_action, priv->tooltips_enabled);
 
-  accel = gtk_accel_group_new();
-  gtk_window_add_accel_group(priv->window, accel);
+  /* Initialize extra widgets */
 
-  menu_item = GTK_WIDGET (gtk_builder_get_object (priv->builder, "load_pictures_menu_item"));
-  gtk_widget_add_accelerator(menu_item, "activate", accel, GDK_KEY_l,
-                             GDK_PRIMARY_MODIFIER, GTK_ACCEL_VISIBLE);
+  /* /\* Accounts menu *\/ */
+  /* priv->accounts_menu_item = */
+  /*   GTK_WIDGET (gtk_builder_get_object (builder, "accounts_menu_item")); */
 
-  menu_item = GTK_WIDGET (gtk_builder_get_object (priv->builder, "edit_details_menu_item"));
-  gtk_widget_add_accelerator(menu_item, "activate", accel, GDK_KEY_d,
-                             GDK_PRIMARY_MODIFIER, GTK_ACCEL_VISIBLE);
+  /* "Add to set" menu needs to be assigned to a var so we control
+     its visibility directly because it has no action assigned to it */
+  priv->add_to_set_menu_item =
+    GTK_WIDGET (gtk_builder_get_object (builder, "add_to_set_menu_item"));
 
-  menu_item = GTK_WIDGET (gtk_builder_get_object (priv->builder, "add_tags_menu_item"));
-  gtk_widget_add_accelerator(menu_item, "activate", accel, GDK_KEY_t,
-                             GDK_PRIMARY_MODIFIER, GTK_ACCEL_VISIBLE);
+  /* populate accounts submenu from model */
+  _populate_accounts_submenu (self);
 
-  menu_item = GTK_WIDGET (gtk_builder_get_object (priv->builder, "add_to_group_menu_item"));
-  gtk_widget_add_accelerator(menu_item, "activate", accel, GDK_KEY_g,
-                             GDK_PRIMARY_MODIFIER, GTK_ACCEL_VISIBLE);
+  /* create contextual menus for right-clicks */
+  priv->pictures_ctxt_menu =
+      GTK_WIDGET (gtk_builder_get_object (builder, "ctxt_menu"));
 
-  menu_item = GTK_WIDGET (gtk_builder_get_object (priv->builder, "add_to_existing_set_menu_item"));
-  gtk_widget_add_accelerator(menu_item, "activate", accel, GDK_KEY_p,
-                             GDK_PRIMARY_MODIFIER, GTK_ACCEL_VISIBLE);
+  /* Initialize drag'n'drop support */
+  _initialize_drag_n_drop (self);
 
-  menu_item = GTK_WIDGET (gtk_builder_get_object (priv->builder, "create_new_set_menu_item"));
-  gtk_widget_add_accelerator(menu_item, "activate", accel, GDK_KEY_p,
-                             GDK_PRIMARY_MODIFIER | GDK_SHIFT_MASK, GTK_ACCEL_VISIBLE);
+  /* Create and hide progress bar dialog for uploading pictures */
+  progress_dialog = gtk_dialog_new_with_buttons (NULL,
+                                                 GTK_WINDOW (self),
+                                                 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
+                                                 GTK_STOCK_CANCEL,
+                                                 GTK_RESPONSE_CANCEL,
+                                                 NULL);
+  gtk_window_set_title (GTK_WINDOW (progress_dialog), APP_SHORTNAME);
 
-  menu_item = GTK_WIDGET (gtk_builder_get_object (priv->builder, "open_in_external_viewer_menu_item"));
-  gtk_widget_add_accelerator(menu_item, "activate", accel, GDK_KEY_v,
-                             GDK_PRIMARY_MODIFIER | GDK_SHIFT_MASK, GTK_ACCEL_VISIBLE);
+  gtk_dialog_set_response_sensitive (GTK_DIALOG (progress_dialog),
+                                     GTK_RESPONSE_CANCEL, TRUE);
 
-  menu_item = GTK_WIDGET (gtk_builder_get_object (priv->builder, "upload_all_menu_item"));
-  gtk_widget_add_accelerator(menu_item, "activate", accel, GDK_KEY_u,
-                             GDK_PRIMARY_MODIFIER | GDK_SHIFT_MASK, GTK_ACCEL_VISIBLE);
-}
+  gtk_container_set_border_width (GTK_CONTAINER (progress_dialog), 6);
+  gtk_window_set_default_size (GTK_WINDOW (progress_dialog), 250, -1);
 
-static void
-_populate_accounts_submenu (FrogrMainView *self)
-{
-  FrogrMainViewPrivate *priv = NULL;
-  FrogrAccount *account = NULL;
-  GtkWidget *menu_item = NULL;
-  GSList *accounts = NULL;
-  GSList *item = NULL;
-  gchar *account_str = NULL;
+  progress_vbox = gtk_dialog_get_content_area (GTK_DIALOG (progress_dialog));
 
-  priv = FROGR_MAIN_VIEW_GET_PRIVATE (self);
-  priv->accounts_menu = NULL;
+  progress_label = gtk_label_new (NULL);
+  gtk_box_pack_start (GTK_BOX (progress_vbox), progress_label, FALSE, FALSE, 6);
 
-  accounts = frogr_controller_get_all_accounts (priv->controller);
-  if (g_slist_length (accounts) > 0)
-    priv->accounts_menu = gtk_menu_new ();
+  progress_bar = gtk_progress_bar_new ();
+  gtk_progress_bar_set_show_text (GTK_PROGRESS_BAR (progress_bar), TRUE);
 
-  for (item = accounts; item; item = g_slist_next (item))
-    {
-      account = FROGR_ACCOUNT (item->data);
+  gtk_box_pack_start (GTK_BOX (progress_vbox), progress_bar, FALSE, FALSE, 6);
 
-      /* Do not use the full name here since it could be the same for
-         different users, thus wouldn't be useful at all for the
-         matter of selecting one account or another. */
-      account_str = g_strdup (frogr_account_get_username (account));
-      menu_item = gtk_check_menu_item_new_with_label (account_str);
-      g_free (account_str);
+  gtk_widget_hide (progress_dialog);
+  priv->progress_dialog = progress_dialog;
+  priv->progress_bar = progress_bar;
+  priv->progress_label = progress_label;
+  priv->progress_is_showing = FALSE;
+  priv->state_description = NULL;
 
-      g_object_set_data (G_OBJECT (menu_item), "frogr-account", account);
+  /* Initialize model */
+  priv->tree_model = GTK_TREE_MODEL (gtk_list_store_new (3,
+                                                         G_TYPE_STRING,
+                                                         GDK_TYPE_PIXBUF,
+                                                         G_TYPE_POINTER));
+  gtk_icon_view_set_model (GTK_ICON_VIEW (icon_view), priv->tree_model);
+  gtk_icon_view_set_pixbuf_column (GTK_ICON_VIEW (icon_view), PIXBUF_COL);
+  gtk_icon_view_set_selection_mode (GTK_ICON_VIEW (icon_view),
+                                    GTK_SELECTION_MULTIPLE);
+  gtk_icon_view_set_columns (GTK_ICON_VIEW (icon_view), -1);
+  gtk_icon_view_set_item_width (GTK_ICON_VIEW (icon_view), IV_THUMB_WIDTH + IV_THUMB_PADDING);
+  gtk_icon_view_set_item_padding (GTK_ICON_VIEW (icon_view), IV_THUMB_PADDING);
+  gtk_icon_view_set_column_spacing (GTK_ICON_VIEW (icon_view), IV_THUMB_PADDING);
+  gtk_icon_view_set_row_spacing (GTK_ICON_VIEW (icon_view), IV_THUMB_PADDING);
+  gtk_widget_set_has_tooltip (icon_view, TRUE);
 
-      g_signal_connect (G_OBJECT (menu_item), "activate",
-                        G_CALLBACK (_on_account_menu_item_toggled),
-                        self);
-      gtk_menu_shell_append (GTK_MENU_SHELL (priv->accounts_menu), menu_item); 
+  gtk_window_set_default_size (GTK_WINDOW (self), MINIMUM_WINDOW_WIDTH, MINIMUM_WINDOW_HEIGHT);
+
+  /* Init status bar */
+  priv->sb_context_id =
+    gtk_statusbar_get_context_id (GTK_STATUSBAR (priv->status_bar),
+                                  "Status bar messages");
+
+  /* Connect signals */
+  g_signal_connect (G_OBJECT (self), "delete-event",
+                    G_CALLBACK (_on_main_view_delete_event),
+                    self);
+
+  g_signal_connect (G_OBJECT (priv->icon_view), "query-tooltip",
+                    G_CALLBACK (_on_icon_view_query_tooltip),
+                    self);
+
+  g_signal_connect (G_OBJECT (priv->icon_view), "selection-changed",
+                    G_CALLBACK (_on_icon_view_selection_changed),
+                    self);
+
+  g_signal_connect (G_OBJECT (priv->progress_dialog), "response",
+                    G_CALLBACK(_progress_dialog_response),
+                    self);
+
+  g_signal_connect (G_OBJECT (priv->progress_dialog),
+                    "delete-event",
+                    G_CALLBACK(_progress_dialog_delete_event),
+                    self);
+
+  gtk_builder_connect_signals (builder, self);
+
+#ifdef MAC_INTEGRATION
+  _tweak_menu_bar_for_mac (self);
+#endif
+
+  /* Update window title */
+  _update_window_title (self, FALSE);
+
+  /* Update UI */
+  _update_ui (FROGR_MAIN_VIEW (self));
+
+  /* Show the auth dialog, if needed, on idle */
+  g_idle_add ((GSourceFunc) _maybe_show_auth_dialog_on_idle, self);
+}
+
+static gboolean
+_maybe_show_auth_dialog_on_idle (FrogrMainView *self)
+{
+  FrogrMainViewPrivate *priv = FROGR_MAIN_VIEW_GET_PRIVATE (self);
+
+  if (!frogr_controller_is_authorized (priv->controller))
+    {
+      /* Show authorization dialog if needed */
+      frogr_controller_show_auth_dialog (priv->controller);
     }
 
-  /* gtk_menu_item_set_submenu (GTK_MENU_ITEM (priv->accounts_menu_item), priv->accounts_menu); */
+  return FALSE;
+}
 
-  if (priv->accounts_menu)
-    gtk_widget_show_all (priv->accounts_menu);
+static void
+_load_project_action (GSimpleAction *action,
+                      GVariant *parameter,
+                      gpointer data)
+{
+  _load_project_dialog (FROGR_MAIN_VIEW (data));
 }
 
 static void
-_initialize_drag_n_drop (FrogrMainView *self)
+_save_project_action (GSimpleAction *action,
+                       GVariant *parameter,
+                       gpointer data)
 {
-  FrogrMainViewPrivate *priv = FROGR_MAIN_VIEW_GET_PRIVATE (self);
+  _save_current_project (FROGR_MAIN_VIEW (data));
+}
 
-  gtk_drag_dest_set (priv->icon_view, GTK_DEST_DEFAULT_ALL,
-                     NULL, 0, GDK_ACTION_COPY );
-  gtk_drag_dest_add_uri_targets (priv->icon_view);
+static void
+_save_project_as_action (GSimpleAction *action,
+                         GVariant *parameter,
+                         gpointer data)
+{
+  _save_project_as_dialog (FROGR_MAIN_VIEW (data));
+}
 
-  g_signal_connect(G_OBJECT(priv->icon_view), "drag-data-received",
-                   G_CALLBACK(_on_icon_view_drag_data_received),
-                   self);
+static void
+_authorize_action (GSimpleAction *action,
+                   GVariant *parameter,
+                   gpointer data)
+{
+  FrogrMainViewPrivate *priv = NULL;
+  priv = FROGR_MAIN_VIEW_GET_PRIVATE (data);
+  frogr_controller_show_auth_dialog (priv->controller);
 }
 
 static void
-_on_icon_view_drag_data_received (GtkWidget *widget,
-                                  GdkDragContext *context,
-                                  gint x, gint y,
-                                  GtkSelectionData *selection_data,
-                                  guint info, guint time,
-                                  gpointer data)
+_preferences_action (GSimpleAction *action,
+                     GVariant *parameter,
+                     gpointer data)
 {
-  FrogrMainView *self = NULL;
   FrogrMainViewPrivate *priv = NULL;
-  GdkAtom target;
-  GSList *fileuris_list = NULL;
-  const guchar *files_string = NULL;
-  gchar **fileuris_array = NULL;
-  gint i;
+  priv = FROGR_MAIN_VIEW_GET_PRIVATE (data);
+  frogr_controller_show_settings_dialog (priv->controller);
+}
+
+static void
+_about_action (GSimpleAction *action,
+               GVariant *parameter,
+               gpointer data)
+{
+  FrogrMainViewPrivate *priv = NULL;
+  priv = FROGR_MAIN_VIEW_GET_PRIVATE (data);
+  frogr_controller_show_about_dialog (priv->controller);
+}
+
+static void
+_help_action (GSimpleAction *action,
+              GVariant *parameter,
+              gpointer data)
+{
+  frogr_util_open_uri ("ghelp:frogr");
+}
+
+static void
+_quit_action (GSimpleAction *action,
+              GVariant *parameter,
+              gpointer data)
+{
+  _quit_application (FROGR_MAIN_VIEW (data));
+}
+
+static void
+_update_project_path (FrogrMainView *self, const gchar *path)
+{
+  FrogrMainViewPrivate *priv = FROGR_MAIN_VIEW_GET_PRIVATE (self);
+
+  /* Early return if nothing changed */
+  if (!g_strcmp0 (priv->project_filepath, path))
+      return;
+
+  g_free (priv->project_name);
+  g_free (priv->project_dir);
+  g_free (priv->project_filepath);
+
+  if (!path)
+    {
+      priv->project_name = NULL;
+      priv->project_dir = NULL;
+      priv->project_filepath = NULL;
+    }
+  else
+    {
+      GFile *file = NULL;
+      GFile *dir = NULL;
+      GFileInfo *file_info = NULL;
+      gchar *dir_path = NULL;
+      const gchar *home_dir = NULL;
+
+      /* Get the display name in beautiful UTF-8 */
+      file = g_file_new_for_path (path);
+      file_info = g_file_query_info (file,
+                                     G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME,
+                                     G_FILE_QUERY_INFO_NONE,
+                                     NULL,
+                                     NULL);
+      priv->project_name = g_strdup (g_file_info_get_display_name (file_info));
 
-  self = FROGR_MAIN_VIEW (data);
-  priv = FROGR_MAIN_VIEW_GET_PRIVATE (data);
+      /* Get the base directory, in beautiful UTF-8 too */
+      dir = g_file_get_parent (file);
+      dir_path = g_file_get_parse_name (dir);
+      home_dir = g_get_home_dir ();
+      if (g_str_has_prefix (dir_path, home_dir))
+        {
+          gchar *tmp_path = NULL;
 
-  /* Do nothing when the application is busy doing something else */
-  if (FROGR_STATE_IS_BUSY(frogr_controller_get_state (priv->controller)))
-    return;
+          tmp_path = dir_path;
+          dir_path = g_strdup_printf ("~%s", &dir_path[g_utf8_strlen (home_dir, -1)]);
+          g_free (tmp_path);
+        }
+      priv->project_dir = dir_path;
 
-  target = gtk_selection_data_get_target (selection_data);
+      /* Finally, store the raw path too */
+      priv->project_filepath = g_strdup (path);
 
-  if (!gtk_targets_include_uri (&target, 1))
-    return;
+      g_object_unref (file);
+      g_object_unref (dir);
+    }
+}
 
-  /* Get GSList with the list of files */
-  files_string = gtk_selection_data_get_data (selection_data);
+static void
+_update_window_title (FrogrMainView *self, gboolean dirty)
+{
+  FrogrMainViewPrivate *priv = FROGR_MAIN_VIEW_GET_PRIVATE (self);
 
-  fileuris_array = g_strsplit ((const gchar*)files_string, "\r\n", -1);
-  for (i = 0;  fileuris_array[i]; i++)
-    {
-      if (fileuris_array[i] && fileuris_array[i][0] != '\0')
-        fileuris_list = g_slist_append (fileuris_list, g_strdup (fileuris_array[i]));
-    }
+  gchar *session_string = NULL;
+  gchar *window_title = NULL;
 
-  /* Load pictures */
-  if (fileuris_list != NULL)
-    _load_pictures (FROGR_MAIN_VIEW (self), fileuris_list);
+  session_string = priv->project_name
+    ? g_strdup_printf ("%s%s (%s) - ", (dirty ? "*" : ""),
+                       priv->project_name, priv->project_dir)
+    : g_strdup("");
 
-  /* Finish drag and drop */
-  gtk_drag_finish (context, TRUE, FALSE, time);
+  window_title = g_strdup_printf ("%s%s", session_string, APP_SHORTNAME);
+  g_free (session_string);
 
-  /* Free */
-  g_strfreev (fileuris_array);
+  gtk_window_set_title (GTK_WINDOW (self), window_title);
+  g_free (window_title);
 }
 
-void
-_on_action_activated (GtkAction *action, gpointer data)
+#ifdef MAC_INTEGRATION
+static gboolean
+osx_can_activate_cb(GtkWidget* widget, guint signal_id, gpointer data)
 {
-  FrogrMainView *mainview = FROGR_MAIN_VIEW (data);
-  FrogrMainViewPrivate *priv = NULL;
-
-  priv = FROGR_MAIN_VIEW_GET_PRIVATE (data);
-  if (action == priv->load_project_action)
-    _load_project_dialog (mainview);
-  else if (action == priv->save_project_action)
-    _save_current_project (mainview);
-  else if (action == priv->save_project_as_action)
-    _save_project_as_dialog (mainview);
-  else if (action == priv->load_pictures_action)
-    _load_pictures_dialog (mainview);
-  else if (action == priv->remove_pictures_action)
-    _remove_selected_pictures (mainview);
-  else if (action == priv->upload_pictures_action)
-    _upload_pictures (mainview);
-  else if (action == priv->open_in_external_viewer_action)
-    _open_pictures_in_external_viewer (mainview);
-  else if (action == priv->add_tags_action)
-    _add_tags_to_pictures (mainview);
-  else if (action == priv->edit_details_action)
-    _edit_selected_pictures (mainview);
-  else if (action == priv->add_to_group_action)
-    _add_pictures_to_group (mainview);
-  else if (action == priv->add_to_set_action)
-    _add_pictures_to_existing_set (mainview);
-  else if (action == priv->add_to_new_set_action)
-    _add_pictures_to_new_set (mainview);
+  return gtk_widget_is_sensitive(widget);
 }
 
-void
-_on_toggle_action_changed (GtkToggleAction *action,
-                           gpointer data)
+static void
+_tweak_menu_bar_for_mac (FrogrMainView *self)
 {
-  gboolean checked;
-  FrogrMainView *mainview = FROGR_MAIN_VIEW (data);
   FrogrMainViewPrivate *priv = NULL;
+  GtkOSXApplication *osx_app = NULL;
+  GtkWidget *menu_item = NULL;
 
-  priv = FROGR_MAIN_VIEW_GET_PRIVATE (data);
-
-  checked = gtk_toggle_action_get_active (action);
-  if (action == priv->enable_tooltips_action)
-    {
-      frogr_config_set_mainview_enable_tooltips (priv->config, checked);
-      priv->tooltips_enabled = checked;
-    }
-  else if (action == priv->reversed_order_action)
-    {
-      _reorder_pictures (mainview, priv->sorting_criteria, checked);
-      frogr_config_set_mainview_sorting_reversed (priv->config, checked);
-    }
-  else if (checked)
-    {
-      /* Radio buttons handling here (only care about 'em when checked) */
-
-      SortingCriteria criteria = SORT_AS_LOADED;
-
-      if (action == priv->sort_by_title_action)
-        criteria = SORT_BY_TITLE;
-      else if (action == priv->sort_by_date_taken_action)
-        criteria = SORT_BY_DATE;
+  priv = FROGR_MAIN_VIEW_GET_PRIVATE (self);
 
-      _reorder_pictures (mainview, criteria, priv->sorting_reversed);
-      frogr_config_set_mainview_sorting_criteria (priv->config, criteria);
-    }
+  osx_app = g_object_new (GTK_TYPE_OSX_APPLICATION, NULL);
+  gtk_osxapplication_set_menu_bar (osx_app, GTK_MENU_SHELL(priv->menu_bar));
 
-  /* State for check menu items should be immediately stored */
-  frogr_config_save_settings (priv->config);
-}
+  /* Relocate the 'about' menu item in the app menu */
+  menu_item = GTK_WIDGET (gtk_builder_get_object (priv->builder, "about_menu_item"));
+  gtk_osxapplication_insert_app_menu_item (osx_app, menu_item, 0);
+  gtk_osxapplication_insert_app_menu_item (osx_app, gtk_separator_menu_item_new (), 1);
+  gtk_widget_show_all (menu_item);
 
-gboolean
-_on_icon_view_key_press_event (GtkWidget *widget,
-                               GdkEventKey *event,
-                               gpointer data)
-{
-  FrogrMainView *mainview = FROGR_MAIN_VIEW (data);
-  FrogrMainViewPrivate *priv = FROGR_MAIN_VIEW_GET_PRIVATE (data);
+  /* Relocate the 'authorize' menu item in the app menu */
+  menu_item = GTK_WIDGET (gtk_builder_get_object (priv->builder, "authorize_menu_item"));
+  gtk_osxapplication_insert_app_menu_item (osx_app, menu_item, 2);
+  gtk_widget_show_all (menu_item);
 
-  /* Actions are only allowed in IDLE state */
-  if (frogr_controller_get_state (priv->controller) != FROGR_STATE_IDLE)
-    return TRUE;
+  /* Relocate the 'accounts' menu item in the app menu */
+  menu_item = GTK_WIDGET (gtk_builder_get_object (priv->builder, "accounts_menu_item"));
+  gtk_osxapplication_insert_app_menu_item (osx_app, menu_item, 3);
+  gtk_osxapplication_insert_app_menu_item (osx_app, gtk_separator_menu_item_new (), 4);
+  gtk_widget_show_all (menu_item);
 
-  /* Do nothing if there's no picture loaded yet */
-  if (!_n_pictures (mainview))
-    return TRUE;
+  /* Relocate the 'preferences' menu item in the app menu */
+  menu_item = GTK_WIDGET (gtk_builder_get_object (priv->builder, "preferences_menu_item"));
+  gtk_osxapplication_insert_app_menu_item (osx_app, menu_item, 5);
+  gtk_osxapplication_insert_app_menu_item (osx_app, gtk_separator_menu_item_new (), 6);
+  gtk_widget_show_all (menu_item);
 
-  /* Remove selected pictures if pressed Supr */
-  if ((event->type == GDK_KEY_PRESS) && (event->keyval == GDK_KEY_Delete))
-    _remove_selected_pictures (mainview);
+  /* Hide the traditional application menu that won't be shown in the Mac */
+  menu_item = GTK_WIDGET (gtk_builder_get_object (priv->builder, "frogr_menu_item"));
+  gtk_widget_hide (menu_item);
 
-  /* Show contextual menu if pressed the 'Menu' key */
-  if (event->type == GDK_KEY_PRESS && event->keyval == GDK_KEY_Menu
-      && priv->n_selected_pictures > 0)
-    {
-      GtkMenu *menu = GTK_MENU (priv->pictures_ctxt_menu);
-      gtk_menu_popup (menu, NULL, NULL, NULL, NULL,
-                      0, gtk_get_current_event_time ());
-    }
+  /* Make sure accelerators are visible when needed */
+  g_signal_connect(priv->menu_bar, "can-activate-accel",
+                   G_CALLBACK(osx_can_activate_cb), NULL);
 
-  return FALSE;
+  gtk_osxapplication_ready(osx_app);
 }
+#endif
 
-gboolean
-_on_icon_view_button_press_event (GtkWidget *widget,
-                                  GdkEventButton *event,
-                                  gpointer data)
+static void
+_setup_keyboard_shortcuts (FrogrMainView *self)
 {
-  FrogrMainView *mainview = FROGR_MAIN_VIEW (data);
-  FrogrMainViewPrivate *priv = FROGR_MAIN_VIEW_GET_PRIVATE (data);
-  GtkTreePath *path;
-
-  /* Actions are only allowed in IDLE state */
-  if (frogr_controller_get_state (priv->controller) != FROGR_STATE_IDLE)
-    return TRUE;
+  FrogrMainViewPrivate *priv = NULL;
+  GtkAccelGroup *accel = NULL;
+  GtkWidget *menu_item = NULL;
 
-  /* Do nothing if there's no picture loaded yet */
-  if (!_n_pictures (mainview))
-    return TRUE;
+  priv = FROGR_MAIN_VIEW_GET_PRIVATE (self);
 
-  /* Check if we clicked on top of an item */
-  if (gtk_icon_view_get_item_at_pos (GTK_ICON_VIEW (priv->icon_view),
-                                     event->x,
-                                     event->y,
-                                     &path,
-                                     NULL))
-    {
-      gboolean path_selected =
-        gtk_icon_view_path_is_selected (GTK_ICON_VIEW (priv->icon_view), path);
+  accel = gtk_accel_group_new();
+  gtk_window_add_accel_group(GTK_WINDOW (self), accel);
 
-      /* Check whether it's needed to keep this item as the only selection */
-      if (((event->button == 1) && path_selected)
-          || ((event->button == 3) && !path_selected))
-        {
-          if (!(event->state & GDK_SHIFT_MASK)
-              && !(event->state & GDK_CONTROL_MASK))
-            {
-              /* Deselect all items if not pressing Ctrl or shift */
-              gtk_icon_view_unselect_all (GTK_ICON_VIEW (priv->icon_view));
-            }
+  menu_item = GTK_WIDGET (gtk_builder_get_object (priv->builder, "load_pictures_menu_item"));
+  gtk_widget_add_accelerator(menu_item, "activate", accel, GDK_KEY_l,
+                             GDK_PRIMARY_MODIFIER, GTK_ACCEL_VISIBLE);
 
-          /* Now select the item */
-          gtk_icon_view_select_path (GTK_ICON_VIEW (priv->icon_view), path);
+  menu_item = GTK_WIDGET (gtk_builder_get_object (priv->builder, "edit_details_menu_item"));
+  gtk_widget_add_accelerator(menu_item, "activate", accel, GDK_KEY_d,
+                             GDK_PRIMARY_MODIFIER, GTK_ACCEL_VISIBLE);
 
-        }
+  menu_item = GTK_WIDGET (gtk_builder_get_object (priv->builder, "add_tags_menu_item"));
+  gtk_widget_add_accelerator(menu_item, "activate", accel, GDK_KEY_t,
+                             GDK_PRIMARY_MODIFIER, GTK_ACCEL_VISIBLE);
 
-      /* Perform the right action: edit picture or show ctxt menu */
-      if ((event->button == 1)                   /* left button */
-          && (event->type == GDK_2BUTTON_PRESS ) /* doubleclick */
-          && !(event->state & GDK_SHIFT_MASK)    /*  not shift  */
-          && !(event->state & GDK_CONTROL_MASK)) /*  not Ctrl */
-        {
-          /* edit selected item */
-          _edit_selected_pictures (mainview);
-        }
-      else if ((event->button == 3)                  /* right button */
-               && (event->type == GDK_BUTTON_PRESS)) /* single click */
-        {
-          /* Show contextual menu */
-          gtk_menu_popup (GTK_MENU (priv->pictures_ctxt_menu),
-                          NULL, NULL, NULL, NULL,
-                          event->button,
-                          gtk_get_current_event_time ());
-        }
+  menu_item = GTK_WIDGET (gtk_builder_get_object (priv->builder, "add_to_group_menu_item"));
+  gtk_widget_add_accelerator(menu_item, "activate", accel, GDK_KEY_g,
+                             GDK_PRIMARY_MODIFIER, GTK_ACCEL_VISIBLE);
 
-      /* Free */
-      gtk_tree_path_free (path);
-    }
+  menu_item = GTK_WIDGET (gtk_builder_get_object (priv->builder, "add_to_existing_set_menu_item"));
+  gtk_widget_add_accelerator(menu_item, "activate", accel, GDK_KEY_p,
+                             GDK_PRIMARY_MODIFIER, GTK_ACCEL_VISIBLE);
 
-  return FALSE;
+  menu_item = GTK_WIDGET (gtk_builder_get_object (priv->builder, "create_new_set_menu_item"));
+  gtk_widget_add_accelerator(menu_item, "activate", accel, GDK_KEY_p,
+                             GDK_PRIMARY_MODIFIER | GDK_SHIFT_MASK, GTK_ACCEL_VISIBLE);
+
+  menu_item = GTK_WIDGET (gtk_builder_get_object (priv->builder, "open_in_external_viewer_menu_item"));
+  gtk_widget_add_accelerator(menu_item, "activate", accel, GDK_KEY_v,
+                             GDK_PRIMARY_MODIFIER | GDK_SHIFT_MASK, GTK_ACCEL_VISIBLE);
+
+  menu_item = GTK_WIDGET (gtk_builder_get_object (priv->builder, "upload_all_menu_item"));
+  gtk_widget_add_accelerator(menu_item, "activate", accel, GDK_KEY_u,
+                             GDK_PRIMARY_MODIFIER | GDK_SHIFT_MASK, GTK_ACCEL_VISIBLE);
 }
 
-void
-_on_account_menu_item_toggled (GtkWidget *widget, gpointer self)
+static void
+_populate_accounts_submenu (FrogrMainView *self)
 {
   FrogrMainViewPrivate *priv = NULL;
   FrogrAccount *account = NULL;
-  gboolean checked = FALSE;
+  GtkWidget *menu_item = NULL;
+  GSList *accounts = NULL;
+  GSList *item = NULL;
+  gchar *account_str = NULL;
 
   priv = FROGR_MAIN_VIEW_GET_PRIVATE (self);
-  checked = gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (widget));
-  account = g_object_get_data (G_OBJECT (widget), "frogr-account");
+  priv->accounts_menu = NULL;
 
-  /* Only set the account if checked */
-  if (checked && account)
-    {
-      DEBUG ("Selected account %s (%s)",
-             frogr_account_get_id (account),
-             frogr_account_get_username (account));
+  accounts = frogr_controller_get_all_accounts (priv->controller);
+  if (g_slist_length (accounts) > 0)
+    priv->accounts_menu = gtk_menu_new ();
 
-      frogr_controller_set_active_account (priv->controller, account);
-    }
-  else if (account)
+  for (item = accounts; item; item = g_slist_next (item))
     {
-      /* If manually unchecked the currently active account, set it again */
-      FrogrAccount *active_account = frogr_controller_get_active_account (priv->controller);
-      if (frogr_account_equal (active_account, account))
-        gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (widget), TRUE);
+      account = FROGR_ACCOUNT (item->data);
+
+      /* Do not use the full name here since it could be the same for
+         different users, thus wouldn't be useful at all for the
+         matter of selecting one account or another. */
+      account_str = g_strdup (frogr_account_get_username (account));
+      menu_item = gtk_check_menu_item_new_with_label (account_str);
+      g_free (account_str);
+
+      g_object_set_data (G_OBJECT (menu_item), "frogr-account", account);
+
+      g_signal_connect (G_OBJECT (menu_item), "activate",
+                        G_CALLBACK (_on_account_menu_item_toggled),
+                        self);
+      gtk_menu_shell_append (GTK_MENU_SHELL (priv->accounts_menu), menu_item); 
     }
+
+  /* gtk_menu_item_set_submenu (GTK_MENU_ITEM (priv->accounts_menu_item), priv->accounts_menu); */
+
+  if (priv->accounts_menu)
+    gtk_widget_show_all (priv->accounts_menu);
 }
 
 static void
-_quit_application (FrogrMainView *self)
+_initialize_drag_n_drop (FrogrMainView *self)
 {
   FrogrMainViewPrivate *priv = FROGR_MAIN_VIEW_GET_PRIVATE (self);
-  g_application_quit (G_APPLICATION (priv->gtk_app));
-}
 
-static gboolean
-_on_main_view_delete_event (GtkWidget *widget,
-                            GdkEvent *event,
-                            gpointer self)
-{
-  _quit_application (FROGR_MAIN_VIEW (self));
-  return TRUE;
+  gtk_drag_dest_set (priv->icon_view, GTK_DEST_DEFAULT_ALL,
+                     NULL, 0, GDK_ACTION_COPY );
+  gtk_drag_dest_add_uri_targets (priv->icon_view);
+
+  g_signal_connect(G_OBJECT(priv->icon_view), "drag-data-received",
+                   G_CALLBACK(_on_icon_view_drag_data_received),
+                   self);
 }
 
-static gboolean
-_on_icon_view_query_tooltip (GtkWidget *icon_view,
-                             gint x, gint y,
-                             gboolean keyboard_mode,
-                             GtkTooltip *tooltip,
-                             gpointer data)
+static void
+_on_icon_view_drag_data_received (GtkWidget *widget,
+                                  GdkDragContext *context,
+                                  gint x, gint y,
+                                  GtkSelectionData *selection_data,
+                                  guint info, guint time,
+                                  gpointer data)
 {
   FrogrMainView *self = NULL;
   FrogrMainViewPrivate *priv = NULL;
-  GtkTreePath *path;
-  gint bw_x;
-  gint bw_y;
-
-  /* No tooltips in keyboard mode... yet */
-  if (keyboard_mode)
-    return FALSE;
+  GdkAtom target;
+  GSList *fileuris_list = NULL;
+  const guchar *files_string = NULL;
+  gchar **fileuris_array = NULL;
+  gint i;
 
   self = FROGR_MAIN_VIEW (data);
-  priv = FROGR_MAIN_VIEW_GET_PRIVATE (self);
-
-  /* Disabled by configuration */
-  if (!frogr_config_get_mainview_enable_tooltips (priv->config))
-    return FALSE;
-
-  /* Check whether we're asking for a tooltip over a picture */
-  gtk_icon_view_convert_widget_to_bin_window_coords (GTK_ICON_VIEW (icon_view),
-                                                     x, y, &bw_x, &bw_y);
-  if (gtk_icon_view_get_item_at_pos (GTK_ICON_VIEW (icon_view), bw_x, bw_y, &path, NULL))
-    {
-      FrogrPicture *picture;
-      GtkTreeIter iter;
-      gchar *tooltip_str = NULL;
-      gchar *filesize = NULL;
-      gchar *filesize_str = NULL;
-      gchar *filesize_markup = NULL;
-      gchar *datetime_markup = NULL;
-      const gchar *datetime = NULL;
-
-      /* Get needed information */
-      gtk_tree_model_get_iter (priv->tree_model, &iter, path);
-      gtk_tree_model_get (priv->tree_model,
-                          &iter,
-                          FPICTURE_COL, &picture,
-                          -1);
-
-      /* Bail out here if needed */
-      if (!picture || !FROGR_IS_PICTURE (picture))
-        return FALSE;
-
-      /* Build the tooltip text with basic info: title, size */
-      filesize = frogr_util_get_datasize_string (frogr_picture_get_filesize (picture));
-      datetime = frogr_picture_get_datetime (picture);
-      if (datetime)
-        {
-          gchar *datetime_str = NULL;
+  priv = FROGR_MAIN_VIEW_GET_PRIVATE (data);
 
-          /* String showind the date and time a picture was taken */
-          datetime_str = g_strdup_printf (_("Taken: %s"), datetime);
-          datetime_markup = g_strdup_printf ("\n<i>%s</i>", datetime_str);
-          g_free (datetime_str);
-        }
+  /* Do nothing when the application is busy doing something else */
+  if (FROGR_STATE_IS_BUSY(frogr_controller_get_state (priv->controller)))
+    return;
 
-      filesize_str = g_strdup_printf (_("File size: %s"), filesize);
-      filesize_markup = g_strdup_printf ("<i>%s</i>", filesize_str);
+  target = gtk_selection_data_get_target (selection_data);
 
-      tooltip_str = g_strdup_printf ("<b>%s</b>\n%s%s",
-                                     frogr_picture_get_title (picture),
-                                     filesize_markup,
-                                     datetime_markup ? datetime_markup : "");
+  if (!gtk_targets_include_uri (&target, 1))
+    return;
 
-      gtk_tooltip_set_markup (tooltip, tooltip_str);
+  /* Get GSList with the list of files */
+  files_string = gtk_selection_data_get_data (selection_data);
 
-      /* Free memory */
-      gtk_tree_path_free (path);
-      g_free (tooltip_str);
-      g_free (filesize);
-      g_free (filesize_str);
-      g_free (filesize_markup);
-      g_free (datetime_markup);
-      return TRUE;
+  fileuris_array = g_strsplit ((const gchar*)files_string, "\r\n", -1);
+  for (i = 0;  fileuris_array[i]; i++)
+    {
+      if (fileuris_array[i] && fileuris_array[i][0] != '\0')
+        fileuris_list = g_slist_append (fileuris_list, g_strdup (fileuris_array[i]));
     }
 
-  return FALSE;
-}
-
-static void
-_on_icon_view_selection_changed (GtkWidget *icon_view, gpointer data)
-{
-  FrogrMainView *self = FROGR_MAIN_VIEW (data);
-  FrogrMainViewPrivate *priv = FROGR_MAIN_VIEW_GET_PRIVATE (self);
-  GList *selected_pictures = NULL;
-  gint len = 0;
-
-  /* We save the value here to avoid traversing all the list whenever
-     we need to check the number of selected pictures */
-  selected_pictures =
-    gtk_icon_view_get_selected_items (GTK_ICON_VIEW (priv->icon_view));
-
-  len = g_list_length (selected_pictures);
-
-  g_list_foreach (selected_pictures, (GFunc)gtk_tree_path_free, NULL);
-  g_list_free (selected_pictures);
+  /* Load pictures */
+  if (fileuris_list != NULL)
+    _load_pictures (FROGR_MAIN_VIEW (self), fileuris_list);
 
-  priv->n_selected_pictures = len;
+  /* Finish drag and drop */
+  gtk_drag_finish (context, TRUE, FALSE, time);
 
-  /* Update sensitiveness for actions */
-  _update_sensitiveness (self);
+  /* Free */
+  g_strfreev (fileuris_array);
 }
 
-static GSList *
-_get_selected_pictures (FrogrMainView *self)
+void
+_on_action_activated (GtkAction *action, gpointer data)
 {
-  FrogrMainViewPrivate *priv = FROGR_MAIN_VIEW_GET_PRIVATE (self);
-  GSList *pictures = NULL;
-  GList *selected_pictures;
-  GList *item;
-
-  /* Iterate over selected items */
-  selected_pictures =
-    gtk_icon_view_get_selected_items (GTK_ICON_VIEW (priv->icon_view));
-  for (item = selected_pictures; item; item = g_list_next (item))
-    {
-      FrogrPicture *picture;
-      GtkTreePath *path;
-      GtkTreeIter iter;
-
-      /* Get needed information */
-      path = (GtkTreePath *)(item->data);
-      gtk_tree_model_get_iter (priv->tree_model, &iter, path);
-      gtk_tree_model_get (priv->tree_model,
-                          &iter,
-                          FPICTURE_COL, &picture,
-                          -1);
-
-      /* Add the picture to the list */
-      pictures = g_slist_prepend (pictures, g_object_ref (picture));
-      gtk_tree_path_free (path);
-    }
-
-  g_list_free (selected_pictures);
+  FrogrMainView *mainview = FROGR_MAIN_VIEW (data);
+  FrogrMainViewPrivate *priv = NULL;
 
-  return pictures;
+  priv = FROGR_MAIN_VIEW_GET_PRIVATE (data);
+  if (action == priv->load_project_action)
+    _load_project_dialog (mainview);
+  else if (action == priv->save_project_action)
+    _save_current_project (mainview);
+  else if (action == priv->save_project_as_action)
+    _save_project_as_dialog (mainview);
+  else if (action == priv->load_pictures_action)
+    _load_pictures_dialog (mainview);
+  else if (action == priv->remove_pictures_action)
+    _remove_selected_pictures (mainview);
+  else if (action == priv->upload_pictures_action)
+    _upload_pictures (mainview);
+  else if (action == priv->open_in_external_viewer_action)
+    _open_pictures_in_external_viewer (mainview);
+  else if (action == priv->add_tags_action)
+    _add_tags_to_pictures (mainview);
+  else if (action == priv->edit_details_action)
+    _edit_selected_pictures (mainview);
+  else if (action == priv->add_to_group_action)
+    _add_pictures_to_group (mainview);
+  else if (action == priv->add_to_set_action)
+    _add_pictures_to_existing_set (mainview);
+  else if (action == priv->add_to_new_set_action)
+    _add_pictures_to_new_set (mainview);
 }
 
-static gint
-_n_pictures (FrogrMainView *self)
+void
+_on_toggle_action_changed (GtkToggleAction *action,
+                           gpointer data)
 {
-  FrogrMainViewPrivate *priv = FROGR_MAIN_VIEW_GET_PRIVATE (self);
-
-  /* Just return the number of pictures in the model */
-  return frogr_model_n_pictures (priv->model);
-}
+  gboolean checked;
+  FrogrMainView *mainview = FROGR_MAIN_VIEW (data);
+  FrogrMainViewPrivate *priv = NULL;
 
-static void
-_load_project_dialog_response_cb (GtkDialog *dialog,
-                                  gint response,
-                                  gpointer data)
-{
-  FrogrMainView *self = FROGR_MAIN_VIEW (data);
+  priv = FROGR_MAIN_VIEW_GET_PRIVATE (data);
 
-  if (response == GTK_RESPONSE_ACCEPT)
+  checked = gtk_toggle_action_get_active (action);
+  if (action == priv->enable_tooltips_action)
     {
-      gchar *filename = NULL;
+      frogr_config_set_mainview_enable_tooltips (priv->config, checked);
+      priv->tooltips_enabled = checked;
+    }
+  else if (action == priv->reversed_order_action)
+    {
+      _reorder_pictures (mainview, priv->sorting_criteria, checked);
+      frogr_config_set_mainview_sorting_reversed (priv->config, checked);
+    }
+  else if (checked)
+    {
+      /* Radio buttons handling here (only care about 'em when checked) */
 
-      filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
-      if (filename != NULL)
-        {
-          FrogrMainViewPrivate *priv = FROGR_MAIN_VIEW_GET_PRIVATE (self);
+      SortingCriteria criteria = SORT_AS_LOADED;
 
-          /* Load from disk and update project's path */
-          frogr_controller_load_project_from_file (priv->controller, filename);
-          _update_project_path (self, filename);
+      if (action == priv->sort_by_title_action)
+        criteria = SORT_BY_TITLE;
+      else if (action == priv->sort_by_date_taken_action)
+        criteria = SORT_BY_DATE;
 
-          g_free (filename);
-        }
+      _reorder_pictures (mainview, criteria, priv->sorting_reversed);
+      frogr_config_set_mainview_sorting_criteria (priv->config, criteria);
     }
 
-  gtk_widget_destroy (GTK_WIDGET (dialog));
+  /* State for check menu items should be immediately stored */
+  frogr_config_save_settings (priv->config);
 }
 
-static void
-_load_project_dialog (FrogrMainView *self)
+gboolean
+_on_icon_view_key_press_event (GtkWidget *widget,
+                               GdkEventKey *event,
+                               gpointer data)
 {
-  FrogrMainViewPrivate *priv = FROGR_MAIN_VIEW_GET_PRIVATE (self);
-  GtkWidget *dialog;
-  GtkFileFilter *filter;
+  FrogrMainView *mainview = FROGR_MAIN_VIEW (data);
+  FrogrMainViewPrivate *priv = FROGR_MAIN_VIEW_GET_PRIVATE (data);
 
-  dialog = gtk_file_chooser_dialog_new (_("Select File"),
-                                        GTK_WINDOW (priv->window),
-                                        GTK_FILE_CHOOSER_ACTION_SAVE,
-                                        GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
-                                        GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
-                                        NULL);
+  /* Actions are only allowed in IDLE state */
+  if (frogr_controller_get_state (priv->controller) != FROGR_STATE_IDLE)
+    return TRUE;
 
-  gtk_file_chooser_set_select_multiple (GTK_FILE_CHOOSER (dialog), FALSE);
-  gtk_file_chooser_set_local_only (GTK_FILE_CHOOSER (dialog), TRUE);
+  /* Do nothing if there's no picture loaded yet */
+  if (!_n_pictures (mainview))
+    return TRUE;
 
-  /* Let's allow text files only */
-  filter = gtk_file_filter_new ();
-  gtk_file_filter_add_mime_type (filter, "text/*");
-  gtk_file_filter_set_name (filter, _("Text Files"));
-  gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
+  /* Remove selected pictures if pressed Supr */
+  if ((event->type == GDK_KEY_PRESS) && (event->keyval == GDK_KEY_Delete))
+    _remove_selected_pictures (mainview);
 
-  g_signal_connect (G_OBJECT (dialog), "response",
-                    G_CALLBACK (_load_project_dialog_response_cb), self);
+  /* Show contextual menu if pressed the 'Menu' key */
+  if (event->type == GDK_KEY_PRESS && event->keyval == GDK_KEY_Menu
+      && priv->n_selected_pictures > 0)
+    {
+      GtkMenu *menu = GTK_MENU (priv->pictures_ctxt_menu);
+      gtk_menu_popup (menu, NULL, NULL, NULL, NULL,
+                      0, gtk_get_current_event_time ());
+    }
 
-  gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
-  gtk_widget_show_all (dialog);
+  return FALSE;
 }
 
-static void
-_save_current_project (FrogrMainView *self)
+gboolean
+_on_icon_view_button_press_event (GtkWidget *widget,
+                                  GdkEventButton *event,
+                                  gpointer data)
 {
-  FrogrMainViewPrivate *priv = FROGR_MAIN_VIEW_GET_PRIVATE (self);
+  FrogrMainView *mainview = FROGR_MAIN_VIEW (data);
+  FrogrMainViewPrivate *priv = FROGR_MAIN_VIEW_GET_PRIVATE (data);
+  GtkTreePath *path;
 
-  if (priv->project_filepath)
-    _save_project_to_file (self, priv->project_filepath);
-  else
-    _save_project_as_dialog (self);
-}
+  /* Actions are only allowed in IDLE state */
+  if (frogr_controller_get_state (priv->controller) != FROGR_STATE_IDLE)
+    return TRUE;
 
-static void
-_save_project_to_file (FrogrMainView *self, const gchar *filepath)
-{
-  FrogrMainViewPrivate *priv = FROGR_MAIN_VIEW_GET_PRIVATE (self);
+  /* Do nothing if there's no picture loaded yet */
+  if (!_n_pictures (mainview))
+    return TRUE;
 
-  /* Save to disk and update project's path */
-  frogr_controller_save_project_to_file (priv->controller, filepath);
-  _update_project_path (self, filepath);
+  /* Check if we clicked on top of an item */
+  if (gtk_icon_view_get_item_at_pos (GTK_ICON_VIEW (priv->icon_view),
+                                     event->x,
+                                     event->y,
+                                     &path,
+                                     NULL))
+    {
+      gboolean path_selected =
+        gtk_icon_view_path_is_selected (GTK_ICON_VIEW (priv->icon_view), path);
 
-  /* Update title marking it as non-dirty (just saved) */
-  _update_window_title (self, FALSE);
-}
+      /* Check whether it's needed to keep this item as the only selection */
+      if (((event->button == 1) && path_selected)
+          || ((event->button == 3) && !path_selected))
+        {
+          if (!(event->state & GDK_SHIFT_MASK)
+              && !(event->state & GDK_CONTROL_MASK))
+            {
+              /* Deselect all items if not pressing Ctrl or shift */
+              gtk_icon_view_unselect_all (GTK_ICON_VIEW (priv->icon_view));
+            }
 
-static void
-_save_project_as_dialog_response_cb (GtkDialog *dialog, gint response, gpointer data)
-{
-  FrogrMainView *self = FROGR_MAIN_VIEW (data);
+          /* Now select the item */
+          gtk_icon_view_select_path (GTK_ICON_VIEW (priv->icon_view), path);
 
-  if (response == GTK_RESPONSE_ACCEPT)
-    {
-      gchar *filename = NULL;
+        }
 
-      filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
-      if (filename != NULL)
-        _save_project_to_file (self, filename);
+      /* Perform the right action: edit picture or show ctxt menu */
+      if ((event->button == 1)                   /* left button */
+          && (event->type == GDK_2BUTTON_PRESS ) /* doubleclick */
+          && !(event->state & GDK_SHIFT_MASK)    /*  not shift  */
+          && !(event->state & GDK_CONTROL_MASK)) /*  not Ctrl */
+        {
+          /* edit selected item */
+          _edit_selected_pictures (mainview);
+        }
+      else if ((event->button == 3)                  /* right button */
+               && (event->type == GDK_BUTTON_PRESS)) /* single click */
+        {
+          /* Show contextual menu */
+          gtk_menu_popup (GTK_MENU (priv->pictures_ctxt_menu),
+                          NULL, NULL, NULL, NULL,
+                          event->button,
+                          gtk_get_current_event_time ());
+        }
 
-      g_free (filename);
+      /* Free */
+      gtk_tree_path_free (path);
     }
 
-  gtk_widget_destroy (GTK_WIDGET (dialog));
+  return FALSE;
 }
 
-static void
-_save_project_as_dialog (FrogrMainView *self)
+void
+_on_account_menu_item_toggled (GtkWidget *widget, gpointer self)
 {
-  FrogrMainViewPrivate *priv = FROGR_MAIN_VIEW_GET_PRIVATE (self);
-  GtkWidget *dialog;
-
-  dialog = gtk_file_chooser_dialog_new (_("Select Destination"),
-                                        GTK_WINDOW (priv->window),
-                                        GTK_FILE_CHOOSER_ACTION_SAVE,
-                                        GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
-                                        GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
-                                        NULL);
-
-  gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (dialog), _("Untitled Project"));
-  gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (dialog), TRUE);
-  gtk_file_chooser_set_select_multiple (GTK_FILE_CHOOSER (dialog), FALSE);
-  gtk_file_chooser_set_local_only (GTK_FILE_CHOOSER (dialog), TRUE);
-
-  g_signal_connect (G_OBJECT (dialog), "response",
-                    G_CALLBACK (_save_project_as_dialog_response_cb), self);
-
-  gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
-  gtk_widget_show_all (dialog);
-}
+  FrogrMainViewPrivate *priv = NULL;
+  FrogrAccount *account = NULL;
+  gboolean checked = FALSE;
 
-static void
-_load_pictures_dialog_response_cb (GtkDialog *dialog, gint response, gpointer data)
-{
-  FrogrMainView *self = FROGR_MAIN_VIEW (data);
+  priv = FROGR_MAIN_VIEW_GET_PRIVATE (self);
+  checked = gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (widget));
+  account = g_object_get_data (G_OBJECT (widget), "frogr-account");
 
-  if (response == GTK_RESPONSE_ACCEPT)
+  /* Only set the account if checked */
+  if (checked && account)
     {
-      GSList *fileuris;
+      DEBUG ("Selected account %s (%s)",
+             frogr_account_get_id (account),
+             frogr_account_get_username (account));
 
-      /* Add selected pictures to icon view area */
-      fileuris = gtk_file_chooser_get_uris (GTK_FILE_CHOOSER (dialog));
-      if (fileuris != NULL)
-        _load_pictures (FROGR_MAIN_VIEW (self), fileuris);
+      frogr_controller_set_active_account (priv->controller, account);
+    }
+  else if (account)
+    {
+      /* If manually unchecked the currently active account, set it again */
+      FrogrAccount *active_account = frogr_controller_get_active_account (priv->controller);
+      if (frogr_account_equal (active_account, account))
+        gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (widget), TRUE);
     }
-
-  gtk_widget_destroy (GTK_WIDGET (dialog));
 }
 
 static void
-_load_pictures_dialog (FrogrMainView *self)
+_quit_application (FrogrMainView *self)
 {
-  FrogrMainViewPrivate *priv = FROGR_MAIN_VIEW_GET_PRIVATE (self);
-  GtkWidget *dialog;
-  GtkFileFilter *all_filter;
-  GtkFileFilter *image_filter;
-  GtkFileFilter *video_filter;
-  gint i;
+  GtkApplication *gtk_app = gtk_window_get_application (GTK_WINDOW (self));
+  g_application_quit (G_APPLICATION (gtk_app));
+}
 
-#ifdef MAC_INTEGRATION
-  const gchar * const *supported_images;
-  const gchar * const *supported_videos;
-#else
-  const gchar * const *supported_mimetypes;
-#endif
+static gboolean
+_on_main_view_delete_event (GtkWidget *widget,
+                            GdkEvent *event,
+                            gpointer self)
+{
+  _quit_application (FROGR_MAIN_VIEW (self));
+  return TRUE;
+}
 
-  dialog = gtk_file_chooser_dialog_new (_("Select a Picture"),
-                                        GTK_WINDOW (priv->window),
-                                        GTK_FILE_CHOOSER_ACTION_OPEN,
-                                        GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
-                                        GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
-                                        NULL);
+static gboolean
+_on_icon_view_query_tooltip (GtkWidget *icon_view,
+                             gint x, gint y,
+                             gboolean keyboard_mode,
+                             GtkTooltip *tooltip,
+                             gpointer data)
+{
+  FrogrMainView *self = NULL;
+  FrogrMainViewPrivate *priv = NULL;
+  GtkTreePath *path;
+  gint bw_x;
+  gint bw_y;
 
-  /* Set images filter */
-  all_filter = gtk_file_filter_new ();
-  image_filter = gtk_file_filter_new ();
-  video_filter = gtk_file_filter_new ();
+  /* No tooltips in keyboard mode... yet */
+  if (keyboard_mode)
+    return FALSE;
 
-#ifdef MAC_INTEGRATION
-  /* Workaround for Mac OSX, where GNOME VFS daemon won't be running,
-     so we can't check filter by mime type (will be text/plain) */
-  supported_images = frogr_util_get_supported_images ();
-  for (i = 0; supported_images[i]; i++)
-    {
-      gtk_file_filter_add_pattern (image_filter, supported_images[i]);
-      gtk_file_filter_add_pattern (all_filter, supported_images[i]);
-    }
+  self = FROGR_MAIN_VIEW (data);
+  priv = FROGR_MAIN_VIEW_GET_PRIVATE (self);
 
-  supported_videos = frogr_util_get_supported_videos ();
-  for (i = 0; supported_videos[i]; i++)
-    {
-      gtk_file_filter_add_pattern (video_filter, supported_videos[i]);
-      gtk_file_filter_add_pattern (all_filter, supported_videos[i]);
-    }
-#else
-  supported_mimetypes = frogr_util_get_supported_mimetypes ();
-  for (i = 0; supported_mimetypes[i]; i++)
-    {
-      if (g_str_has_prefix (supported_mimetypes[i], "image"))
-        gtk_file_filter_add_mime_type (image_filter, supported_mimetypes[i]);
-      else
-        gtk_file_filter_add_mime_type (video_filter, supported_mimetypes[i]);
+  /* Disabled by configuration */
+  if (!frogr_config_get_mainview_enable_tooltips (priv->config))
+    return FALSE;
 
-      gtk_file_filter_add_mime_type (all_filter, supported_mimetypes[i]);
-    }
-#endif
+  /* Check whether we're asking for a tooltip over a picture */
+  gtk_icon_view_convert_widget_to_bin_window_coords (GTK_ICON_VIEW (icon_view),
+                                                     x, y, &bw_x, &bw_y);
+  if (gtk_icon_view_get_item_at_pos (GTK_ICON_VIEW (icon_view), bw_x, bw_y, &path, NULL))
+    {
+      FrogrPicture *picture;
+      GtkTreeIter iter;
+      gchar *tooltip_str = NULL;
+      gchar *filesize = NULL;
+      gchar *filesize_str = NULL;
+      gchar *filesize_markup = NULL;
+      gchar *datetime_markup = NULL;
+      const gchar *datetime = NULL;
 
-  gtk_file_filter_set_name (all_filter, _("All Files"));
-  gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), all_filter);
+      /* Get needed information */
+      gtk_tree_model_get_iter (priv->tree_model, &iter, path);
+      gtk_tree_model_get (priv->tree_model,
+                          &iter,
+                          FPICTURE_COL, &picture,
+                          -1);
 
-  gtk_file_filter_set_name (image_filter, _("Image Files"));
-  gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), image_filter);
+      /* Bail out here if needed */
+      if (!picture || !FROGR_IS_PICTURE (picture))
+        return FALSE;
 
-  gtk_file_filter_set_name (video_filter, _("Video Files"));
-  gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), video_filter);
+      /* Build the tooltip text with basic info: title, size */
+      filesize = frogr_util_get_datasize_string (frogr_picture_get_filesize (picture));
+      datetime = frogr_picture_get_datetime (picture);
+      if (datetime)
+        {
+          gchar *datetime_str = NULL;
 
-  gtk_file_chooser_set_select_multiple (GTK_FILE_CHOOSER (dialog), TRUE);
-  gtk_file_chooser_set_local_only (GTK_FILE_CHOOSER (dialog), FALSE);
+          /* String showind the date and time a picture was taken */
+          datetime_str = g_strdup_printf (_("Taken: %s"), datetime);
+          datetime_markup = g_strdup_printf ("\n<i>%s</i>", datetime_str);
+          g_free (datetime_str);
+        }
 
-  g_signal_connect (G_OBJECT (dialog), "response",
-                    G_CALLBACK (_load_pictures_dialog_response_cb), self);
+      filesize_str = g_strdup_printf (_("File size: %s"), filesize);
+      filesize_markup = g_strdup_printf ("<i>%s</i>", filesize_str);
 
-  gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
-  gtk_widget_show_all (dialog);
-}
+      tooltip_str = g_strdup_printf ("<b>%s</b>\n%s%s",
+                                     frogr_picture_get_title (picture),
+                                     filesize_markup,
+                                     datetime_markup ? datetime_markup : "");
 
-static gboolean
-_pictures_selected_required_check (FrogrMainView *self)
-{
-  FrogrMainViewPrivate *priv = FROGR_MAIN_VIEW_GET_PRIVATE (self);
+      gtk_tooltip_set_markup (tooltip, tooltip_str);
 
-  if (priv->n_selected_pictures == 0)
-    {
-      frogr_util_show_error_dialog (priv->window,
-                                    _("You need to select some pictures first"));
-      return FALSE;
+      /* Free memory */
+      gtk_tree_path_free (path);
+      g_free (tooltip_str);
+      g_free (filesize);
+      g_free (filesize_str);
+      g_free (filesize_markup);
+      g_free (datetime_markup);
+      return TRUE;
     }
 
-  return TRUE;
+  return FALSE;
 }
 
 static void
-_add_tags_to_pictures (FrogrMainView *self)
+_on_icon_view_selection_changed (GtkWidget *icon_view, gpointer data)
 {
+  FrogrMainView *self = FROGR_MAIN_VIEW (data);
   FrogrMainViewPrivate *priv = FROGR_MAIN_VIEW_GET_PRIVATE (self);
+  GList *selected_pictures = NULL;
+  gint len = 0;
 
-  if (!_pictures_selected_required_check (self))
-    return;
+  /* We save the value here to avoid traversing all the list whenever
+     we need to check the number of selected pictures */
+  selected_pictures =
+    gtk_icon_view_get_selected_items (GTK_ICON_VIEW (priv->icon_view));
 
-  /* Call the controller to add tags to them */
-  frogr_controller_show_add_tags_dialog (priv->controller,
-                                         _get_selected_pictures (self));
-}
+  len = g_list_length (selected_pictures);
 
-static void
-_add_pictures_to_new_set (FrogrMainView *self)
-{
-  FrogrMainViewPrivate *priv = FROGR_MAIN_VIEW_GET_PRIVATE (self);
+  g_list_foreach (selected_pictures, (GFunc)gtk_tree_path_free, NULL);
+  g_list_free (selected_pictures);
 
-  if (!_pictures_selected_required_check (self))
-    return;
+  priv->n_selected_pictures = len;
 
-  /* Call the controller to add the pictures to sets */
-  frogr_controller_show_create_new_set_dialog (priv->controller,
-                                               _get_selected_pictures (self));
+  /* Update sensitiveness for actions */
+  _update_sensitiveness (self);
 }
 
-static void
-_add_pictures_to_existing_set (FrogrMainView *self)
+static GSList *
+_get_selected_pictures (FrogrMainView *self)
 {
   FrogrMainViewPrivate *priv = FROGR_MAIN_VIEW_GET_PRIVATE (self);
+  GSList *pictures = NULL;
+  GList *selected_pictures;
+  GList *item;
 
-  if (!_pictures_selected_required_check (self))
-    return;
+  /* Iterate over selected items */
+  selected_pictures =
+    gtk_icon_view_get_selected_items (GTK_ICON_VIEW (priv->icon_view));
+  for (item = selected_pictures; item; item = g_list_next (item))
+    {
+      FrogrPicture *picture;
+      GtkTreePath *path;
+      GtkTreeIter iter;
 
-  /* Call the controller to add the pictures to sets */
-  frogr_controller_show_add_to_set_dialog (priv->controller,
-                                           _get_selected_pictures (self));
-}
+      /* Get needed information */
+      path = (GtkTreePath *)(item->data);
+      gtk_tree_model_get_iter (priv->tree_model, &iter, path);
+      gtk_tree_model_get (priv->tree_model,
+                          &iter,
+                          FPICTURE_COL, &picture,
+                          -1);
 
-static void
-_add_pictures_to_group (FrogrMainView *self)
-{
-  FrogrMainViewPrivate *priv = FROGR_MAIN_VIEW_GET_PRIVATE (self);
+      /* Add the picture to the list */
+      pictures = g_slist_prepend (pictures, g_object_ref (picture));
+      gtk_tree_path_free (path);
+    }
 
-  if (!_pictures_selected_required_check (self))
-    return;
+  g_list_free (selected_pictures);
 
-  /* Call the controller to add the pictures to sets */
-  frogr_controller_show_add_to_group_dialog (priv->controller,
-                                             _get_selected_pictures (self));
+  return pictures;
 }
 
-static void
-_edit_selected_pictures (FrogrMainView *self)
+static gint
+_n_pictures (FrogrMainView *self)
 {
   FrogrMainViewPrivate *priv = FROGR_MAIN_VIEW_GET_PRIVATE (self);
 
-  if (!_pictures_selected_required_check (self))
-    return;
-
-  /* Call the controller to edit them */
-  frogr_controller_show_details_dialog (priv->controller,
-                                        _get_selected_pictures (self));
+  /* Just return the number of pictures in the model */
+  return frogr_model_n_pictures (priv->model);
 }
 
 static void
-_open_pictures_in_external_viewer (FrogrMainView *self)
+_load_project_dialog_response_cb (GtkDialog *dialog,
+                                  gint response,
+                                  gpointer data)
 {
-  GSList *pictures = NULL;
+  FrogrMainView *self = FROGR_MAIN_VIEW (data);
 
-  if (!_pictures_selected_required_check (self))
-    return;
+  if (response == GTK_RESPONSE_ACCEPT)
+    {
+      gchar *filename = NULL;
+
+      filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
+      if (filename != NULL)
+        {
+          FrogrMainViewPrivate *priv = FROGR_MAIN_VIEW_GET_PRIVATE (self);
+
+          /* Load from disk and update project's path */
+          frogr_controller_load_project_from_file (priv->controller, filename);
+          _update_project_path (self, filename);
+
+          g_free (filename);
+        }
+    }
 
-  pictures = _get_selected_pictures (self);
-  frogr_util_open_pictures_in_viewer (pictures);
-  g_slist_free (pictures);
+  gtk_widget_destroy (GTK_WIDGET (dialog));
 }
 
 static void
-_remove_selected_pictures (FrogrMainView *self)
+_load_project_dialog (FrogrMainView *self)
 {
-  FrogrMainViewPrivate *priv = FROGR_MAIN_VIEW_GET_PRIVATE (self);
-  GSList *selected_pictures;
-  GSList *item;
+  GtkWidget *dialog;
+  GtkFileFilter *filter;
 
-  if (!_pictures_selected_required_check (self))
-    return;
+  dialog = gtk_file_chooser_dialog_new (_("Select File"),
+                                        GTK_WINDOW (self),
+                                        GTK_FILE_CHOOSER_ACTION_SAVE,
+                                        GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+                                        GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
+                                        NULL);
 
-  /* Remove from model */
-  selected_pictures = _get_selected_pictures (self);
-  for (item = selected_pictures; item; item = g_slist_next (item))
-    {
-      FrogrPicture *picture = FROGR_PICTURE (item->data);
-      frogr_model_remove_picture (priv->model, picture);
-    }
+  gtk_file_chooser_set_select_multiple (GTK_FILE_CHOOSER (dialog), FALSE);
+  gtk_file_chooser_set_local_only (GTK_FILE_CHOOSER (dialog), TRUE);
 
-  /* Update UI */
-  _update_ui (self);
+  /* Let's allow text files only */
+  filter = gtk_file_filter_new ();
+  gtk_file_filter_add_mime_type (filter, "text/*");
+  gtk_file_filter_set_name (filter, _("Text Files"));
+  gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
 
-  /* Free */
-  g_slist_free (selected_pictures);
+  g_signal_connect (G_OBJECT (dialog), "response",
+                    G_CALLBACK (_load_project_dialog_response_cb), self);
+
+  gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
+  gtk_widget_show_all (dialog);
 }
 
 static void
-_load_pictures (FrogrMainView *self, GSList *fileuris)
+_save_current_project (FrogrMainView *self)
 {
   FrogrMainViewPrivate *priv = FROGR_MAIN_VIEW_GET_PRIVATE (self);
-  frogr_controller_load_pictures (priv->controller, fileuris);
+
+  if (priv->project_filepath)
+    _save_project_to_file (self, priv->project_filepath);
+  else
+    _save_project_as_dialog (self);
 }
 
 static void
-_upload_pictures (FrogrMainView *self)
+_save_project_to_file (FrogrMainView *self, const gchar *filepath)
 {
   FrogrMainViewPrivate *priv = FROGR_MAIN_VIEW_GET_PRIVATE (self);
 
-  gtk_icon_view_unselect_all (GTK_ICON_VIEW (priv->icon_view));
-  frogr_controller_upload_pictures (priv->controller);
+  /* Save to disk and update project's path */
+  frogr_controller_save_project_to_file (priv->controller, filepath);
+  _update_project_path (self, filepath);
+
+  /* Update title marking it as non-dirty (just saved) */
+  _update_window_title (self, FALSE);
 }
 
 static void
-_reorder_pictures (FrogrMainView *self, SortingCriteria criteria, gboolean reversed)
+_save_project_as_dialog_response_cb (GtkDialog *dialog, gint response, gpointer data)
 {
-  FrogrMainViewPrivate *priv = FROGR_MAIN_VIEW_GET_PRIVATE (self);
-  GSList *list_as_loaded = NULL;
-  GSList *current_list = NULL;
-  GSList *current_item = NULL;
-  gint *new_order = 0;
-  gint current_pos = 0;
-  gint new_pos = 0;
+  FrogrMainView *self = FROGR_MAIN_VIEW (data);
 
-  gchar *property_name = NULL;
+  if (response == GTK_RESPONSE_ACCEPT)
+    {
+      gchar *filename = NULL;
 
-  priv->sorting_criteria = criteria;
-  priv->sorting_reversed = reversed;
+      filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
+      if (filename != NULL)
+        _save_project_to_file (self, filename);
 
-  if (!_n_pictures (self))
-    return;
+      g_free (filename);
+    }
 
-  /* Choose the property to sort by */
-  switch (criteria)
-    {
-    case SORT_AS_LOADED:
-      property_name = NULL;
-      break;
+  gtk_widget_destroy (GTK_WIDGET (dialog));
+}
 
-    case SORT_BY_TITLE:
-      property_name = g_strdup ("title");
-      break;
+static void
+_save_project_as_dialog (FrogrMainView *self)
+{
+  GtkWidget *dialog;
 
-    case SORT_BY_DATE:
-      property_name = g_strdup ("datetime");
-      break;
+  dialog = gtk_file_chooser_dialog_new (_("Select Destination"),
+                                        GTK_WINDOW (self),
+                                        GTK_FILE_CHOOSER_ACTION_SAVE,
+                                        GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+                                        GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
+                                        NULL);
 
-    default:
-      g_assert_not_reached ();
-    }
+  gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (dialog), _("Untitled Project"));
+  gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (dialog), TRUE);
+  gtk_file_chooser_set_select_multiple (GTK_FILE_CHOOSER (dialog), FALSE);
+  gtk_file_chooser_set_local_only (GTK_FILE_CHOOSER (dialog), TRUE);
 
-  /* Temporarily save the current list, and alloc an array to
-     represent the new order compared to the old positions */
-  current_list = g_slist_copy (priv->sorted_pictures);
-  new_order = g_new0 (gint, g_slist_length (current_list));
+  g_signal_connect (G_OBJECT (dialog), "response",
+                    G_CALLBACK (_save_project_as_dialog_response_cb), self);
 
-  /* Use the original list (as loaded) as reference for sorting */
-  list_as_loaded = g_slist_copy (frogr_model_get_pictures (priv->model));
-  if (property_name)
-    list_as_loaded = g_slist_sort_with_data (list_as_loaded,
-                                             (GCompareDataFunc) _compare_pictures_by_property,
-                                             (gchar*) property_name);
-  /* Update the list of pictures */
-  if (priv->sorted_pictures)
-    {
-      g_slist_foreach (priv->sorted_pictures, (GFunc)g_object_unref, NULL);
-      g_slist_free (priv->sorted_pictures);
-    }
-  priv->sorted_pictures = list_as_loaded;
-  g_slist_foreach (priv->sorted_pictures, (GFunc)g_object_ref, NULL);
+  gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
+  gtk_widget_show_all (dialog);
+}
 
-  /* If we're reordering in reverse order, reverse the result list */
-  if (reversed)
-    priv->sorted_pictures = g_slist_reverse (priv->sorted_pictures);
+static void
+_load_pictures_dialog_response_cb (GtkDialog *dialog, gint response, gpointer data)
+{
+  FrogrMainView *self = FROGR_MAIN_VIEW (data);
 
-  /* Build the new_order array and update the treeview */
-  current_pos = 0;
-  for (current_item = current_list; current_item; current_item = g_slist_next (current_item))
+  if (response == GTK_RESPONSE_ACCEPT)
     {
-      new_pos = g_slist_index (priv->sorted_pictures, current_item->data);
-      new_order[new_pos] = current_pos++;
+      GSList *fileuris;
+
+      /* Add selected pictures to icon view area */
+      fileuris = gtk_file_chooser_get_uris (GTK_FILE_CHOOSER (dialog));
+      if (fileuris != NULL)
+        _load_pictures (FROGR_MAIN_VIEW (self), fileuris);
     }
-  gtk_list_store_reorder (GTK_LIST_STORE (priv->tree_model), new_order);
 
-  g_slist_free (current_list);
-  g_free (new_order);
-  g_free (property_name);
+  gtk_widget_destroy (GTK_WIDGET (dialog));
 }
 
-static gint
-_compare_pictures_by_property (FrogrPicture *p1, FrogrPicture *p2,
-                               const gchar *property_name)
+static void
+_load_pictures_dialog (FrogrMainView *self)
 {
-  GParamSpec *pspec1 = NULL;
-  GParamSpec *pspec2 = NULL;
-  GValue value1 = { 0 };
-  GValue value2 = { 0 };
-  gint result = 0;
-
-  g_return_val_if_fail (FROGR_IS_PICTURE (p1), 0);
-  g_return_val_if_fail (FROGR_IS_PICTURE (p2), 0);
-
-  pspec1 = g_object_class_find_property (G_OBJECT_GET_CLASS (p1), property_name);
-  pspec2 = g_object_class_find_property (G_OBJECT_GET_CLASS (p2), property_name);
+  GtkWidget *dialog;
+  GtkFileFilter *all_filter;
+  GtkFileFilter *image_filter;
+  GtkFileFilter *video_filter;
+  gint i;
 
-  /* They should be the same! */
-  if (pspec1->value_type != pspec2->value_type)
-    return 0;
+#ifdef MAC_INTEGRATION
+  const gchar * const *supported_images;
+  const gchar * const *supported_videos;
+#else
+  const gchar * const *supported_mimetypes;
+#endif
 
-  g_value_init (&value1, pspec1->value_type);
-  g_value_init (&value2, pspec1->value_type);
+  dialog = gtk_file_chooser_dialog_new (_("Select a Picture"),
+                                        GTK_WINDOW (self),
+                                        GTK_FILE_CHOOSER_ACTION_OPEN,
+                                        GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+                                        GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
+                                        NULL);
 
-  g_object_get_property (G_OBJECT (p1), property_name, &value1);
-  g_object_get_property (G_OBJECT (p2), property_name, &value2);
+  /* Set images filter */
+  all_filter = gtk_file_filter_new ();
+  image_filter = gtk_file_filter_new ();
+  video_filter = gtk_file_filter_new ();
 
-  if (G_VALUE_HOLDS_BOOLEAN (&value1))
-    result = g_value_get_boolean (&value1) - g_value_get_boolean (&value2);
-  else if (G_VALUE_HOLDS_INT (&value1))
-    result = g_value_get_int (&value1) - g_value_get_int (&value2);
-  else if (G_VALUE_HOLDS_LONG (&value1))
-    result = g_value_get_long (&value1) - g_value_get_long (&value2);
-  else if (G_VALUE_HOLDS_STRING (&value1))
+#ifdef MAC_INTEGRATION
+  /* Workaround for Mac OSX, where GNOME VFS daemon won't be running,
+     so we can't check filter by mime type (will be text/plain) */
+  supported_images = frogr_util_get_supported_images ();
+  for (i = 0; supported_images[i]; i++)
     {
-      const gchar *str1 = NULL;
-      const gchar *str2 = NULL;
-      gchar *str1_cf = NULL;
-      gchar *str2_cf = NULL;
+      gtk_file_filter_add_pattern (image_filter, supported_images[i]);
+      gtk_file_filter_add_pattern (all_filter, supported_images[i]);
+    }
 
-      /* Comparison of strings require some additional work to take
-         into account the different rules for each locale */
-      str1 = g_value_get_string (&value1);
-      str2 = g_value_get_string (&value2);
+  supported_videos = frogr_util_get_supported_videos ();
+  for (i = 0; supported_videos[i]; i++)
+    {
+      gtk_file_filter_add_pattern (video_filter, supported_videos[i]);
+      gtk_file_filter_add_pattern (all_filter, supported_videos[i]);
+    }
+#else
+  supported_mimetypes = frogr_util_get_supported_mimetypes ();
+  for (i = 0; supported_mimetypes[i]; i++)
+    {
+      if (g_str_has_prefix (supported_mimetypes[i], "image"))
+        gtk_file_filter_add_mime_type (image_filter, supported_mimetypes[i]);
+      else
+        gtk_file_filter_add_mime_type (video_filter, supported_mimetypes[i]);
 
-      str1_cf = g_utf8_casefold (str1 ? str1 : "", -1);
-      str2_cf = g_utf8_casefold (str2 ? str2 : "", -1);
+      gtk_file_filter_add_mime_type (all_filter, supported_mimetypes[i]);
+    }
+#endif
 
-      result = g_utf8_collate (str1_cf, str2_cf);
+  gtk_file_filter_set_name (all_filter, _("All Files"));
+  gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), all_filter);
 
-      g_free (str1_cf);
-      g_free (str2_cf);
-    }
-  else
-    g_warning ("Unsupported type for property used for sorting");
+  gtk_file_filter_set_name (image_filter, _("Image Files"));
+  gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), image_filter);
 
-  g_value_unset (&value1);
-  g_value_unset (&value2);
+  gtk_file_filter_set_name (video_filter, _("Video Files"));
+  gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), video_filter);
 
-  return result;
-}
+  gtk_file_chooser_set_select_multiple (GTK_FILE_CHOOSER (dialog), TRUE);
+  gtk_file_chooser_set_local_only (GTK_FILE_CHOOSER (dialog), FALSE);
 
-static void
-_progress_dialog_response (GtkDialog *dialog,
-                           gint response_id,
-                           gpointer data)
-{
-  FrogrMainView *self = FROGR_MAIN_VIEW (data);
-  FrogrMainViewPrivate *priv = FROGR_MAIN_VIEW_GET_PRIVATE (self);
+  g_signal_connect (G_OBJECT (dialog), "response",
+                    G_CALLBACK (_load_pictures_dialog_response_cb), self);
 
-  frogr_controller_cancel_ongoing_requests (priv->controller);
-  gtk_widget_hide (priv->progress_dialog);
+  gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
+  gtk_widget_show_all (dialog);
 }
 
 static gboolean
-_progress_dialog_delete_event (GtkWidget *widget,
-                               GdkEvent *event,
-                               gpointer data)
+_pictures_selected_required_check (FrogrMainView *self)
 {
-  FrogrMainView *self = FROGR_MAIN_VIEW (data);
   FrogrMainViewPrivate *priv = FROGR_MAIN_VIEW_GET_PRIVATE (self);
 
-  frogr_controller_cancel_ongoing_requests (priv->controller);
-  gtk_widget_hide (priv->progress_dialog);
+  if (priv->n_selected_pictures == 0)
+    {
+      frogr_util_show_error_dialog (GTK_WINDOW (self),
+                                    _("You need to select some pictures first"));
+      return FALSE;
+    }
 
   return TRUE;
 }
 
 static void
-_controller_state_changed (FrogrController *controller,
-                           FrogrControllerState state,
-                           gpointer data)
+_add_tags_to_pictures (FrogrMainView *self)
 {
-  FrogrMainView *mainview = FROGR_MAIN_VIEW (data);
-  _update_ui (mainview);
+  FrogrMainViewPrivate *priv = FROGR_MAIN_VIEW_GET_PRIVATE (self);
+
+  if (!_pictures_selected_required_check (self))
+    return;
+
+  /* Call the controller to add tags to them */
+  frogr_controller_show_add_tags_dialog (priv->controller,
+                                         _get_selected_pictures (self));
 }
 
 static void
-_controller_active_account_changed (FrogrController *controller,
-                                    FrogrAccount *account,
-                                    gpointer data)
+_add_pictures_to_new_set (FrogrMainView *self)
 {
-  FrogrMainView *mainview = NULL;
+  FrogrMainViewPrivate *priv = FROGR_MAIN_VIEW_GET_PRIVATE (self);
 
-  mainview = FROGR_MAIN_VIEW (data);
-  _update_state_description (mainview);
-  _update_account_menu_items (mainview);
-  _update_ui (mainview);
+  if (!_pictures_selected_required_check (self))
+    return;
+
+  /* Call the controller to add the pictures to sets */
+  frogr_controller_show_create_new_set_dialog (priv->controller,
+                                               _get_selected_pictures (self));
 }
 
 static void
-_controller_accounts_changed (FrogrController *controller,
-                              gpointer data)
+_add_pictures_to_existing_set (FrogrMainView *self)
 {
-  FrogrMainView *mainview = NULL;
+  FrogrMainViewPrivate *priv = FROGR_MAIN_VIEW_GET_PRIVATE (self);
 
-  /* Re-populate the accounts submenu */
-  mainview = FROGR_MAIN_VIEW (data);
-  _populate_accounts_submenu (mainview);
-  _update_ui (mainview);
+  if (!_pictures_selected_required_check (self))
+    return;
 
-  DEBUG ("%s", "Accounts list changed");
+  /* Call the controller to add the pictures to sets */
+  frogr_controller_show_add_to_set_dialog (priv->controller,
+                                           _get_selected_pictures (self));
 }
 
 static void
-_model_picture_added (FrogrController *controller,
-                      FrogrPicture *picture,
-                      gpointer data)
+_add_pictures_to_group (FrogrMainView *self)
 {
-  FrogrMainView *self = FROGR_MAIN_VIEW (data);
   FrogrMainViewPrivate *priv = FROGR_MAIN_VIEW_GET_PRIVATE (self);
-  GdkPixbuf *pixbuf = NULL;
-  const gchar *fileuri = NULL;
-  GtkTreeIter iter;
 
-  /* Add to GtkIconView */
-  fileuri = frogr_picture_get_fileuri (picture);
-  pixbuf = frogr_picture_get_pixbuf (picture);
+  if (!_pictures_selected_required_check (self))
+    return;
 
-  gtk_list_store_append (GTK_LIST_STORE (priv->tree_model), &iter);
-  gtk_list_store_set (GTK_LIST_STORE (priv->tree_model), &iter,
-                      FILEURI_COL, fileuri,
-                      PIXBUF_COL, pixbuf,
-                      FPICTURE_COL, picture,
-                      -1);
+  /* Call the controller to add the pictures to sets */
+  frogr_controller_show_add_to_group_dialog (priv->controller,
+                                             _get_selected_pictures (self));
+}
 
-  /* Update the list */
-  priv->sorted_pictures = g_slist_append (priv->sorted_pictures,
-                                          g_object_ref (picture));
+static void
+_edit_selected_pictures (FrogrMainView *self)
+{
+  FrogrMainViewPrivate *priv = FROGR_MAIN_VIEW_GET_PRIVATE (self);
 
-  /* Reorder if needed */
-  if (priv->sorting_criteria != SORT_AS_LOADED || priv->sorting_reversed)
-    frogr_main_view_reorder_pictures (self);
+  if (!_pictures_selected_required_check (self))
+    return;
 
-  /* Update upload size in state description */
-  _update_state_description (self);
+  /* Call the controller to edit them */
+  frogr_controller_show_details_dialog (priv->controller,
+                                        _get_selected_pictures (self));
 }
 
 static void
-_model_picture_removed (FrogrController *controller,
-                        FrogrPicture *picture,
-                        gpointer data)
+_open_pictures_in_external_viewer (FrogrMainView *self)
 {
-  FrogrMainView *self = FROGR_MAIN_VIEW (data);
-  FrogrMainViewPrivate *priv = NULL;
-  GtkTreeModel *tree_model = NULL;
-  GtkTreeIter iter;
-
-  /* Check items in the icon_view */
-  priv = FROGR_MAIN_VIEW_GET_PRIVATE (self);
+  GSList *pictures = NULL;
 
-  tree_model = gtk_icon_view_get_model (GTK_ICON_VIEW (priv->icon_view));
-  if (gtk_tree_model_get_iter_first (tree_model, &iter))
-    {
-      /* Look for the picture and remove it */
-      do
-        {
-          FrogrPicture *picture_from_ui;
+  if (!_pictures_selected_required_check (self))
+    return;
 
-          /* Get needed information */
-          gtk_tree_model_get (priv->tree_model,
-                              &iter,
-                              FPICTURE_COL, &picture_from_ui,
-                              -1);
+  pictures = _get_selected_pictures (self);
+  frogr_util_open_pictures_in_viewer (pictures);
+  g_slist_free (pictures);
+}
 
-          if (picture_from_ui == picture)
-            {
-              /* Remove from the GtkIconView and break loop */
-              gtk_list_store_remove (GTK_LIST_STORE (priv->tree_model), &iter);
+static void
+_remove_selected_pictures (FrogrMainView *self)
+{
+  FrogrMainViewPrivate *priv = FROGR_MAIN_VIEW_GET_PRIVATE (self);
+  GSList *selected_pictures;
+  GSList *item;
 
-              /* Update the list */
-              priv->sorted_pictures = g_slist_remove (priv->sorted_pictures, picture);
-              g_object_unref (picture);
+  if (!_pictures_selected_required_check (self))
+    return;
 
-              break;
-            }
-        }
-      while (gtk_tree_model_iter_next (tree_model, &iter));
+  /* Remove from model */
+  selected_pictures = _get_selected_pictures (self);
+  for (item = selected_pictures; item; item = g_slist_next (item))
+    {
+      FrogrPicture *picture = FROGR_PICTURE (item->data);
+      frogr_model_remove_picture (priv->model, picture);
     }
 
-  /* Update upload size in state description */
-  _update_state_description (self);
+  /* Update UI */
+  _update_ui (self);
+
+  /* Free */
+  g_slist_free (selected_pictures);
 }
 
 static void
-_model_changed (FrogrController *controller, gpointer data)
+_load_pictures (FrogrMainView *self, GSList *fileuris)
 {
-  /* Reflect that the current state is 'dirty' in the title */
-  _update_window_title (FROGR_MAIN_VIEW (data), TRUE);
+  FrogrMainViewPrivate *priv = FROGR_MAIN_VIEW_GET_PRIVATE (self);
+  frogr_controller_load_pictures (priv->controller, fileuris);
 }
 
 static void
-_model_deserialized (FrogrController *controller, gpointer data)
+_upload_pictures (FrogrMainView *self)
 {
-  /* Reflect that the current state is not 'dirty' (just loaded) */
-  _update_window_title (FROGR_MAIN_VIEW (data), FALSE);
+  FrogrMainViewPrivate *priv = FROGR_MAIN_VIEW_GET_PRIVATE (self);
+
+  gtk_icon_view_unselect_all (GTK_ICON_VIEW (priv->icon_view));
+  frogr_controller_upload_pictures (priv->controller);
 }
 
 static void
-_update_account_menu_items (FrogrMainView *mainview)
+_reorder_pictures (FrogrMainView *self, SortingCriteria criteria, gboolean reversed)
 {
-  FrogrMainViewPrivate *priv = NULL;
+  FrogrMainViewPrivate *priv = FROGR_MAIN_VIEW_GET_PRIVATE (self);
+  GSList *list_as_loaded = NULL;
+  GSList *current_list = NULL;
+  GSList *current_item = NULL;
+  gint *new_order = 0;
+  gint current_pos = 0;
+  gint new_pos = 0;
 
-  priv = FROGR_MAIN_VIEW_GET_PRIVATE (mainview);
-  if (priv->accounts_menu && GTK_IS_CONTAINER (priv->accounts_menu))
+  gchar *property_name = NULL;
+
+  priv->sorting_criteria = criteria;
+  priv->sorting_reversed = reversed;
+
+  if (!_n_pictures (self))
+    return;
+
+  /* Choose the property to sort by */
+  switch (criteria)
     {
-      FrogrAccount *active_account = NULL;
-      FrogrAccount *account = NULL;
-      GList *all_items = NULL;
-      GList *item = NULL;
-      GtkWidget *menu_item = NULL;
+    case SORT_AS_LOADED:
+      property_name = NULL;
+      break;
 
-      active_account = frogr_controller_get_active_account (priv->controller);
-      all_items = gtk_container_get_children (GTK_CONTAINER (priv->accounts_menu));
-      for (item = all_items; item; item = g_list_next (item))
-        {
-          gboolean value;
+    case SORT_BY_TITLE:
+      property_name = g_strdup ("title");
+      break;
 
-          menu_item = GTK_WIDGET (item->data);
-          account = g_object_get_data (G_OBJECT (menu_item), "frogr-account"); 
+    case SORT_BY_DATE:
+      property_name = g_strdup ("datetime");
+      break;
 
-          if (account == active_account)
-            value = TRUE;
-          else
-            value = FALSE;
+    default:
+      g_assert_not_reached ();
+    }
 
-          gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (menu_item), value);
-        }
-      g_list_free (all_items);
+  /* Temporarily save the current list, and alloc an array to
+     represent the new order compared to the old positions */
+  current_list = g_slist_copy (priv->sorted_pictures);
+  new_order = g_new0 (gint, g_slist_length (current_list));
+
+  /* Use the original list (as loaded) as reference for sorting */
+  list_as_loaded = g_slist_copy (frogr_model_get_pictures (priv->model));
+  if (property_name)
+    list_as_loaded = g_slist_sort_with_data (list_as_loaded,
+                                             (GCompareDataFunc) _compare_pictures_by_property,
+                                             (gchar*) property_name);
+  /* Update the list of pictures */
+  if (priv->sorted_pictures)
+    {
+      g_slist_foreach (priv->sorted_pictures, (GFunc)g_object_unref, NULL);
+      g_slist_free (priv->sorted_pictures);
     }
-}
-
-static void
-_update_state_description (FrogrMainView *mainview)
-{
-  FrogrMainViewPrivate *priv = NULL;
+  priv->sorted_pictures = list_as_loaded;
+  g_slist_foreach (priv->sorted_pictures, (GFunc)g_object_ref, NULL);
 
-  priv = FROGR_MAIN_VIEW_GET_PRIVATE (mainview);
+  /* If we're reordering in reverse order, reverse the result list */
+  if (reversed)
+    priv->sorted_pictures = g_slist_reverse (priv->sorted_pictures);
 
-  g_free (priv->state_description);
-  priv->state_description = _craft_state_description (mainview);
+  /* Build the new_order array and update the treeview */
+  current_pos = 0;
+  for (current_item = current_list; current_item; current_item = g_slist_next (current_item))
+    {
+      new_pos = g_slist_index (priv->sorted_pictures, current_item->data);
+      new_order[new_pos] = current_pos++;
+    }
+  gtk_list_store_reorder (GTK_LIST_STORE (priv->tree_model), new_order);
 
-  /* Do not force updating the status bar when loading pictures */
-  if (frogr_controller_get_state (priv->controller) != FROGR_STATE_LOADING_PICTURES)
-    frogr_main_view_set_status_text (mainview, priv->state_description);
+  g_slist_free (current_list);
+  g_free (new_order);
+  g_free (property_name);
 }
 
-static gchar *
-_craft_state_description (FrogrMainView *mainview)
+static gint
+_compare_pictures_by_property (FrogrPicture *p1, FrogrPicture *p2,
+                               const gchar *property_name)
 {
-  FrogrMainViewPrivate *priv = NULL;
-  FrogrAccount *account = NULL;
-  GSList *pictures = NULL;
-  guint n_pictures = 0;
-  const gchar *login = NULL;
-  gchar *description = NULL;
-  gchar *login_str = NULL;
-  gchar *bandwidth_str = NULL;
-  gchar *upload_size_str = NULL;
-  gboolean is_pro = FALSE;
+  GParamSpec *pspec1 = NULL;
+  GParamSpec *pspec2 = NULL;
+  GValue value1 = { 0 };
+  GValue value2 = { 0 };
+  gint result = 0;
 
-  priv = FROGR_MAIN_VIEW_GET_PRIVATE (mainview);
-  account = frogr_controller_get_active_account (priv->controller);
+  g_return_val_if_fail (FROGR_IS_PICTURE (p1), 0);
+  g_return_val_if_fail (FROGR_IS_PICTURE (p2), 0);
 
-  if (!FROGR_IS_ACCOUNT (account) || !frogr_account_has_extra_info (account))
-    return g_strdup (_("Not connected to Flickr"));
+  pspec1 = g_object_class_find_property (G_OBJECT_GET_CLASS (p1), property_name);
+  pspec2 = g_object_class_find_property (G_OBJECT_GET_CLASS (p2), property_name);
 
-  /* Just use the username here ant not the fullname (when available),
-     since it could happen that the full name was the same for two
-     different usernames from the same frogr user, thus making
-     impossible to distinguish which account you are using. */
-  login = frogr_account_get_username (account);
-  is_pro = frogr_account_is_pro (account);
+  /* They should be the same! */
+  if (pspec1->value_type != pspec2->value_type)
+    return 0;
 
-  /* Login string, showing the user is PRO (second '%s') if so. */
-  login_str = g_strdup_printf (_("Connected as %s%s"), login,
-                               (is_pro ? _(" (PRO account)") : ""));
+  g_value_init (&value1, pspec1->value_type);
+  g_value_init (&value2, pspec1->value_type);
 
-  /* Pro users do not have any limit of quota, so it makes no sense to
-     permanently show that they have 2.0 GB / 2.0 GB remaining */
-  if (!is_pro)
+  g_object_get_property (G_OBJECT (p1), property_name, &value1);
+  g_object_get_property (G_OBJECT (p2), property_name, &value2);
+
+  if (G_VALUE_HOLDS_BOOLEAN (&value1))
+    result = g_value_get_boolean (&value1) - g_value_get_boolean (&value2);
+  else if (G_VALUE_HOLDS_INT (&value1))
+    result = g_value_get_int (&value1) - g_value_get_int (&value2);
+  else if (G_VALUE_HOLDS_LONG (&value1))
+    result = g_value_get_long (&value1) - g_value_get_long (&value2);
+  else if (G_VALUE_HOLDS_STRING (&value1))
     {
-      gchar *remaining_bw_str = NULL;
-      gchar *max_bw_str = NULL;
-      gulong remaining_bw;
-      gulong max_bw;
+      const gchar *str1 = NULL;
+      const gchar *str2 = NULL;
+      gchar *str1_cf = NULL;
+      gchar *str2_cf = NULL;
 
-      remaining_bw = frogr_account_get_remaining_bandwidth (account);
-      max_bw = frogr_account_get_max_bandwidth (account);
+      /* Comparison of strings require some additional work to take
+         into account the different rules for each locale */
+      str1 = g_value_get_string (&value1);
+      str2 = g_value_get_string (&value2);
 
-      remaining_bw_str = frogr_util_get_datasize_string (remaining_bw);
-      max_bw_str = frogr_util_get_datasize_string (max_bw);
+      str1_cf = g_utf8_casefold (str1 ? str1 : "", -1);
+      str2_cf = g_utf8_casefold (str2 ? str2 : "", -1);
 
-      if (remaining_bw_str && max_bw_str)
-        {
-          /* Will show in the status bar the amount of data (in KB, MB
-             or GB) the user is currently allowed to upload to flicker
-             till the end of the month, in a CURRENT / MAX fashion.
-             The '-' at the beginning is just a separator, since more
-             blocks of text will be shown in the status bar too. */
-          bandwidth_str = g_strdup_printf (_(" - %s / %s remaining"),
-                                           remaining_bw_str,
-                                           max_bw_str);
-        }
+      result = g_utf8_collate (str1_cf, str2_cf);
 
-      g_free (remaining_bw_str);
-      g_free (max_bw_str);
+      g_free (str1_cf);
+      g_free (str2_cf);
     }
+  else
+    g_warning ("Unsupported type for property used for sorting");
 
-  /* Check size of the loaded pictures, if any */
-  pictures = frogr_model_get_pictures (priv->model);
-  n_pictures = frogr_model_n_pictures (priv->model);
-  if (n_pictures)
-    {
-      GSList *item = NULL;
-      gulong total_size = 0;
-      gchar *total_size_str = NULL;
-
-      for (item = pictures; item; item = g_slist_next (item))
-        total_size += frogr_picture_get_filesize (FROGR_PICTURE (item->data));
-
-      total_size_str = frogr_util_get_datasize_string (total_size);
+  g_value_unset (&value1);
+  g_value_unset (&value2);
 
-      /* Will show in the status bar the amount of pictures and data
-         (in KB, MB or GB) that would be uploaded as the sum of the
-         sizes for every picture loaded in the application */
-      upload_size_str = g_strdup_printf (ngettext (" - %d file to upload (%s)",
-                                                   " - %d files to upload (%s)",
-                                                   n_pictures),
-                                         n_pictures, total_size_str);
-      g_free (total_size_str);
-    }
+  return result;
+}
 
-  /* Build the final string */
-  description = g_strdup_printf ("%s%s%s",
-                                 login_str,
-                                 (bandwidth_str ? bandwidth_str : ""),
-                                 (upload_size_str ? upload_size_str : ""));
-  g_free (login_str);
-  g_free (bandwidth_str);
-  g_free (upload_size_str);
+static void
+_progress_dialog_response (GtkDialog *dialog,
+                           gint response_id,
+                           gpointer data)
+{
+  FrogrMainView *self = FROGR_MAIN_VIEW (data);
+  FrogrMainViewPrivate *priv = FROGR_MAIN_VIEW_GET_PRIVATE (self);
 
-  return description;
+  frogr_controller_cancel_ongoing_requests (priv->controller);
+  gtk_widget_hide (priv->progress_dialog);
 }
 
-static void
-_update_sensitiveness (FrogrMainView *self)
+static gboolean
+_progress_dialog_delete_event (GtkWidget *widget,
+                               GdkEvent *event,
+                               gpointer data)
 {
+  FrogrMainView *self = FROGR_MAIN_VIEW (data);
   FrogrMainViewPrivate *priv = FROGR_MAIN_VIEW_GET_PRIVATE (self);
-  /* gboolean has_accounts = FALSE; */
-  gboolean has_pics = FALSE;
-  gint n_selected_pics = 0;
 
-  /* Set sensitiveness */
-  switch (frogr_controller_get_state (priv->controller))
-    {
-    case FROGR_STATE_LOADING_PICTURES:
-    case FROGR_STATE_UPLOADING_PICTURES:
-      gtk_action_set_sensitive (priv->load_project_action, FALSE);
-      gtk_action_set_sensitive (priv->save_project_action, FALSE);
-      gtk_action_set_sensitive (priv->save_project_as_action, FALSE);
-      gtk_action_set_sensitive (priv->load_pictures_action, FALSE);
-      gtk_action_set_sensitive (priv->remove_pictures_action, FALSE);
-      gtk_action_set_sensitive (priv->upload_pictures_action, FALSE);
-      gtk_action_set_sensitive (priv->open_in_external_viewer_action, FALSE);
-      gtk_action_set_sensitive (priv->add_tags_action, FALSE);
-      gtk_action_set_sensitive (priv->edit_details_action, FALSE);
-      gtk_action_set_sensitive (priv->add_to_group_action, FALSE);
-      gtk_action_set_sensitive (priv->add_to_set_action, FALSE);
-      gtk_action_set_sensitive (priv->add_to_new_set_action, FALSE);
-      /* gtk_widget_set_sensitive (priv->accounts_menu_item, FALSE); */
-      gtk_widget_set_sensitive (priv->add_to_set_menu_item, FALSE);
-      break;
+  frogr_controller_cancel_ongoing_requests (priv->controller);
+  gtk_widget_hide (priv->progress_dialog);
 
-    case FROGR_STATE_IDLE:
-      has_pics = (_n_pictures (self) > 0);
-      /* has_accounts = (priv->accounts_menu != NULL); */
-      n_selected_pics = priv->n_selected_pictures;
+  return TRUE;
+}
 
-      gtk_action_set_sensitive (priv->load_project_action, TRUE);
-      gtk_action_set_sensitive (priv->save_project_action, TRUE);
-      gtk_action_set_sensitive (priv->save_project_as_action, TRUE);
-      gtk_action_set_sensitive (priv->load_pictures_action, TRUE);
-      /* gtk_widget_set_sensitive (priv->accounts_menu_item, has_accounts); */
-      gtk_action_set_sensitive (priv->upload_pictures_action, has_pics);
-      gtk_action_set_sensitive (priv->remove_pictures_action, n_selected_pics > 0);
-      gtk_action_set_sensitive (priv->open_in_external_viewer_action, n_selected_pics > 0);
-      gtk_action_set_sensitive (priv->add_tags_action, n_selected_pics > 0);
-      gtk_action_set_sensitive (priv->edit_details_action, n_selected_pics > 0);
-      gtk_action_set_sensitive (priv->add_to_group_action, n_selected_pics > 0);
-      gtk_action_set_sensitive (priv->add_to_set_action, n_selected_pics > 0);
-      gtk_action_set_sensitive (priv->add_to_new_set_action, n_selected_pics > 0);
-      gtk_widget_set_sensitive (priv->add_to_set_menu_item, n_selected_pics > 0);
-      break;
+static void
+_controller_state_changed (FrogrController *controller,
+                           FrogrControllerState state,
+                           gpointer data)
+{
+  FrogrMainView *mainview = FROGR_MAIN_VIEW (data);
+  _update_ui (mainview);
+}
 
-    default:
-      g_warning ("Invalid state reached!!");
-    }
+static void
+_controller_active_account_changed (FrogrController *controller,
+                                    FrogrAccount *account,
+                                    gpointer data)
+{
+  FrogrMainView *mainview = NULL;
+
+  mainview = FROGR_MAIN_VIEW (data);
+  _update_state_description (mainview);
+  _update_account_menu_items (mainview);
+  _update_ui (mainview);
 }
 
 static void
-_update_ui (FrogrMainView *self)
+_controller_accounts_changed (FrogrController *controller,
+                              gpointer data)
 {
-  FrogrMainViewPrivate *priv = FROGR_MAIN_VIEW_GET_PRIVATE (self);
+  FrogrMainView *mainview = NULL;
 
-  /* Set sensitiveness */
-  _update_sensitiveness (self);
+  /* Re-populate the accounts submenu */
+  mainview = FROGR_MAIN_VIEW (data);
+  _populate_accounts_submenu (mainview);
+  _update_ui (mainview);
 
-  /* Update status bar from model's state description */
-  if (frogr_controller_get_state (priv->controller) == FROGR_STATE_IDLE)
-    frogr_main_view_set_status_text (self, priv->state_description);
+  DEBUG ("%s", "Accounts list changed");
 }
 
 static void
-_frogr_main_view_set_property (GObject *object,
-                               guint prop_id,
-                               const GValue *value,
-                               GParamSpec *pspec)
+_model_picture_added (FrogrController *controller,
+                      FrogrPicture *picture,
+                      gpointer data)
 {
-  FrogrMainView *self = FROGR_MAIN_VIEW (object);
+  FrogrMainView *self = FROGR_MAIN_VIEW (data);
   FrogrMainViewPrivate *priv = FROGR_MAIN_VIEW_GET_PRIVATE (self);
+  GdkPixbuf *pixbuf = NULL;
+  const gchar *fileuri = NULL;
+  GtkTreeIter iter;
 
-  switch (prop_id)
-    {
-    case PROP_GTK_APPLICATION:
-      priv->gtk_app = GTK_APPLICATION (g_value_get_object (value));
-      break;
-    default:
-      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-      break;
-    }
-}
+  /* Add to GtkIconView */
+  fileuri = frogr_picture_get_fileuri (picture);
+  pixbuf = frogr_picture_get_pixbuf (picture);
 
-static void
-_frogr_main_view_get_property (GObject *object,
-                               guint prop_id,
-                               GValue *value,
-                               GParamSpec *pspec)
-{
-  FrogrMainViewPrivate *priv = FROGR_MAIN_VIEW_GET_PRIVATE (object);
+  gtk_list_store_append (GTK_LIST_STORE (priv->tree_model), &iter);
+  gtk_list_store_set (GTK_LIST_STORE (priv->tree_model), &iter,
+                      FILEURI_COL, fileuri,
+                      PIXBUF_COL, pixbuf,
+                      FPICTURE_COL, picture,
+                      -1);
 
-  switch (prop_id)
-    {
-    case PROP_GTK_APPLICATION:
-      g_value_set_object (value, priv->gtk_app);
-      break;
-   default:
-      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-      break;
-    }
-}
+  /* Update the list */
+  priv->sorted_pictures = g_slist_append (priv->sorted_pictures,
+                                          g_object_ref (picture));
 
-static GActionEntry app_entries[] = {
-  { "load_project", _load_project_action, NULL, NULL, NULL },
-  { "save_project", _save_project_action, NULL, NULL, NULL },
-  { "save_project_as", _save_project_as_action, NULL, NULL, NULL },
-  { "authorize", _authorize_action, NULL, NULL, NULL },
-  { "preferences", _preferences_action, NULL, NULL, NULL },
-  { "help", _help_action, NULL, NULL, NULL },
-  { "about", _about_action, NULL, NULL, NULL },
-  { "quit", _quit_action, NULL, NULL, NULL },
-};
+  /* Reorder if needed */
+  if (priv->sorting_criteria != SORT_AS_LOADED || priv->sorting_reversed)
+    frogr_main_view_reorder_pictures (self);
+
+  /* Update upload size in state description */
+  _update_state_description (self);
+}
 
-static GObject *
-_frogr_main_view_constructor (GType type,
-                              guint n_construct_properties,
-                              GObjectConstructParam *construct_properties)
+static void
+_model_picture_removed (FrogrController *controller,
+                        FrogrPicture *picture,
+                        gpointer data)
 {
-  GObject *object = NULL;
-  FrogrMainView *self = NULL;
+  FrogrMainView *self = FROGR_MAIN_VIEW (data);
   FrogrMainViewPrivate *priv = NULL;
-  GtkBuilder *builder;
-  GtkWidget *main_vbox;
-  GtkWidget *icon_view;
-  GtkWidget *status_bar;
-  GtkWidget *progress_dialog;
-  GtkWidget *progress_vbox;
-  GtkWidget *progress_bar;
-  GtkWidget *progress_label;
-  GtkWidget *toolbar;
-  gchar *full_path;
-
-  /* Chain up to the parent's constructor first */
-  object =
-    G_OBJECT_CLASS (frogr_main_view_parent_class)->constructor (type,
-                                                                 n_construct_properties,
-                                                                 construct_properties);
-  self = FROGR_MAIN_VIEW (object);
-  priv = FROGR_MAIN_VIEW_GET_PRIVATE (self);
-
-  /* Get widgets from GtkBuilder */
-  builder = gtk_builder_new ();
-  priv->builder = builder;
-
-  full_path = g_strdup_printf ("%s/" UI_MAIN_VIEW_FILE, frogr_util_get_app_data_dir ());
-  gtk_builder_add_from_file (builder, full_path, NULL);
-  g_free (full_path);
+  GtkTreeModel *tree_model = NULL;
+  GtkTreeIter iter;
 
-  priv->window = GTK_WINDOW (gtk_application_window_new (GTK_APPLICATION (priv->gtk_app)));
-  gtk_application_window_set_show_menubar (GTK_APPLICATION_WINDOW (priv->window), TRUE);
+  /* Check items in the icon_view */
+  priv = FROGR_MAIN_VIEW_GET_PRIVATE (self);
 
-  main_vbox = GTK_WIDGET (gtk_builder_get_object (builder, "main_window_vbox"));
-  gtk_container_add (GTK_CONTAINER (priv->window), main_vbox);
+  tree_model = gtk_icon_view_get_model (GTK_ICON_VIEW (priv->icon_view));
+  if (gtk_tree_model_get_iter_first (tree_model, &iter))
+    {
+      /* Look for the picture and remove it */
+      do
+        {
+          FrogrPicture *picture_from_ui;
 
-  /* Menu bar */
-  priv->menu_bar = GTK_WIDGET (gtk_builder_get_object (builder, "menu_bar"));
-  gtk_widget_show_all (priv->menu_bar);
+          /* Get needed information */
+          gtk_tree_model_get (priv->tree_model,
+                              &iter,
+                              FPICTURE_COL, &picture_from_ui,
+                              -1);
 
-#ifndef MAC_INTEGRATION
-  gtk_box_pack_start (GTK_BOX (main_vbox), priv->menu_bar, FALSE, FALSE, 0);
-  gtk_box_reorder_child (GTK_BOX (main_vbox), priv->menu_bar, 0);
-#endif
+          if (picture_from_ui == picture)
+            {
+              /* Remove from the GtkIconView and break loop */
+              gtk_list_store_remove (GTK_LIST_STORE (priv->tree_model), &iter);
 
-  /* App menu */
-  full_path = g_strdup_printf ("%s/" UI_APP_MENU_FILE, frogr_util_get_app_data_dir ());
-  gtk_builder_add_from_file (builder, full_path, NULL);
-  g_free (full_path);
-  g_action_map_add_action_entries (G_ACTION_MAP (priv->gtk_app),
-                                   app_entries, G_N_ELEMENTS (app_entries),
-                                   self);
-  gtk_application_set_app_menu (GTK_APPLICATION (priv->gtk_app),
-                                G_MENU_MODEL (gtk_builder_get_object (builder, "app-menu")));
+              /* Update the list */
+              priv->sorted_pictures = g_slist_remove (priv->sorted_pictures, picture);
+              g_object_unref (picture);
 
-  toolbar = GTK_WIDGET (gtk_builder_get_object (builder, "toolbar"));
-  gtk_style_context_add_class (gtk_widget_get_style_context (toolbar),
-                               GTK_STYLE_CLASS_PRIMARY_TOOLBAR);
+              break;
+            }
+        }
+      while (gtk_tree_model_iter_next (tree_model, &iter));
+    }
 
-  icon_view = GTK_WIDGET (gtk_builder_get_object (builder, "icon_view"));
-  priv->icon_view = icon_view;
+  /* Update upload size in state description */
+  _update_state_description (self);
+}
 
-  status_bar = GTK_WIDGET (gtk_builder_get_object (builder, "status_bar"));
-  priv->status_bar = status_bar;
+static void
+_model_changed (FrogrController *controller, gpointer data)
+{
+  /* Reflect that the current state is 'dirty' in the title */
+  _update_window_title (FROGR_MAIN_VIEW (data), TRUE);
+}
 
-  /* Get actions from GtkBuilder */
-  priv->load_project_action =
-    GTK_ACTION (gtk_builder_get_object (builder, "load_project_action"));
-  priv->save_project_action =
-    GTK_ACTION (gtk_builder_get_object (builder, "save_project_action"));
-  priv->save_project_as_action =
-    GTK_ACTION (gtk_builder_get_object (builder, "save_project_as_action"));
-  priv->load_pictures_action =
-    GTK_ACTION (gtk_builder_get_object (builder, "load_pictures_action"));
-  priv->remove_pictures_action =
-    GTK_ACTION (gtk_builder_get_object (builder, "remove_pictures_action"));
-  priv->upload_pictures_action =
-    GTK_ACTION (gtk_builder_get_object (builder, "upload_pictures_action"));
-  priv->open_in_external_viewer_action =
-    GTK_ACTION (gtk_builder_get_object (builder,
-                                        "open_in_external_viewer_action"));
-  priv->auth_action =
-    GTK_ACTION (gtk_builder_get_object (builder, "auth_action"));
-  priv->preferences_action =
-    GTK_ACTION (gtk_builder_get_object (builder, "preferences_action"));
-  priv->add_tags_action =
-    GTK_ACTION (gtk_builder_get_object (builder, "add_tags_action"));
-  priv->edit_details_action =
-    GTK_ACTION (gtk_builder_get_object (builder, "edit_details_action"));
-  priv->add_to_group_action =
-    GTK_ACTION (gtk_builder_get_object (builder, "add_to_group_action"));
-  priv->add_to_set_action =
-    GTK_ACTION (gtk_builder_get_object (builder, "add_to_set_action"));
-  priv->add_to_new_set_action =
-    GTK_ACTION (gtk_builder_get_object (builder, "add_to_new_set_action"));
-  priv->help_action =
-    GTK_ACTION (gtk_builder_get_object (builder, "help_action"));
-  priv->about_action =
-    GTK_ACTION (gtk_builder_get_object (builder, "about_action"));
-  priv->enable_tooltips_action =
-    GTK_TOGGLE_ACTION (gtk_builder_get_object (builder,
-                                               "enable_tooltips_action"));
-  priv->sort_by_title_action =
-    GTK_TOGGLE_ACTION (gtk_builder_get_object (builder,
-                                               "sort_by_title_action"));
-  priv->sort_by_date_taken_action =
-    GTK_TOGGLE_ACTION (gtk_builder_get_object (builder,
-                                               "sort_by_date_taken_action"));
-  priv->sort_as_loaded_action =
-    GTK_TOGGLE_ACTION (gtk_builder_get_object (builder,
-                                               "sort_as_loaded_action"));
-  priv->reversed_order_action =
-    GTK_TOGGLE_ACTION (gtk_builder_get_object (builder,
-                                               "reversed_order_action"));
-  /* Set Keyboard shortcuts */
-  _setup_keyboard_shortcuts (self);
+static void
+_model_deserialized (FrogrController *controller, gpointer data)
+{
+  /* Reflect that the current state is not 'dirty' (just loaded) */
+  _update_window_title (FROGR_MAIN_VIEW (data), FALSE);
+}
 
-  /* Init main model's state description */
-  _update_state_description (self);
+static void
+_update_account_menu_items (FrogrMainView *mainview)
+{
+  FrogrMainViewPrivate *priv = NULL;
 
-  /* Init the details of the current project */
-  _update_project_path (self, NULL);
+  priv = FROGR_MAIN_VIEW_GET_PRIVATE (mainview);
+  if (priv->accounts_menu && GTK_IS_CONTAINER (priv->accounts_menu))
+    {
+      FrogrAccount *active_account = NULL;
+      FrogrAccount *account = NULL;
+      GList *all_items = NULL;
+      GList *item = NULL;
+      GtkWidget *menu_item = NULL;
 
-  /* Initialize sorting criteria and reverse in the UI */
-  if (priv->sorting_criteria == SORT_BY_TITLE)
-    gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (priv->sort_by_title_action), TRUE);
-  else if (priv->sorting_criteria == SORT_BY_DATE)
-    gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (priv->sort_by_date_taken_action), TRUE);
-  else
-    gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (priv->sort_as_loaded_action), TRUE);
+      active_account = frogr_controller_get_active_account (priv->controller);
+      all_items = gtk_container_get_children (GTK_CONTAINER (priv->accounts_menu));
+      for (item = all_items; item; item = g_list_next (item))
+        {
+          gboolean value;
 
-  gtk_toggle_action_set_active (priv->reversed_order_action, priv->sorting_reversed);
+          menu_item = GTK_WIDGET (item->data);
+          account = g_object_get_data (G_OBJECT (menu_item), "frogr-account"); 
 
-  /* Initialize 'tooltips enabled' in the UI */
-  gtk_toggle_action_set_active (priv->enable_tooltips_action, priv->tooltips_enabled);
+          if (account == active_account)
+            value = TRUE;
+          else
+            value = FALSE;
 
-  /* Initialize extra widgets */
+          gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (menu_item), value);
+        }
+      g_list_free (all_items);
+    }
+}
 
-  /* /\* Accounts menu *\/ */
-  /* priv->accounts_menu_item = */
-  /*   GTK_WIDGET (gtk_builder_get_object (builder, "accounts_menu_item")); */
+static void
+_update_state_description (FrogrMainView *mainview)
+{
+  FrogrMainViewPrivate *priv = NULL;
 
-  /* "Add to set" menu needs to be assigned to a var so we control
-     its visibility directly because it has no action assigned to it */
-  priv->add_to_set_menu_item =
-    GTK_WIDGET (gtk_builder_get_object (builder, "add_to_set_menu_item"));
+  priv = FROGR_MAIN_VIEW_GET_PRIVATE (mainview);
 
-  /* populate accounts submenu from model */
-  _populate_accounts_submenu (self);
+  g_free (priv->state_description);
+  priv->state_description = _craft_state_description (mainview);
 
-  /* create contextual menus for right-clicks */
-  priv->pictures_ctxt_menu =
-      GTK_WIDGET (gtk_builder_get_object (builder, "ctxt_menu"));
+  /* Do not force updating the status bar when loading pictures */
+  if (frogr_controller_get_state (priv->controller) != FROGR_STATE_LOADING_PICTURES)
+    frogr_main_view_set_status_text (mainview, priv->state_description);
+}
 
-  /* Initialize drag'n'drop support */
-  _initialize_drag_n_drop (self);
+static gchar *
+_craft_state_description (FrogrMainView *mainview)
+{
+  FrogrMainViewPrivate *priv = NULL;
+  FrogrAccount *account = NULL;
+  GSList *pictures = NULL;
+  guint n_pictures = 0;
+  const gchar *login = NULL;
+  gchar *description = NULL;
+  gchar *login_str = NULL;
+  gchar *bandwidth_str = NULL;
+  gchar *upload_size_str = NULL;
+  gboolean is_pro = FALSE;
 
-  /* Create and hide progress bar dialog for uploading pictures */
-  progress_dialog = gtk_dialog_new_with_buttons (NULL,
-                                                 priv->window,
-                                                 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
-                                                 GTK_STOCK_CANCEL,
-                                                 GTK_RESPONSE_CANCEL,
-                                                 NULL);
-  gtk_window_set_title (GTK_WINDOW (progress_dialog), APP_SHORTNAME);
+  priv = FROGR_MAIN_VIEW_GET_PRIVATE (mainview);
+  account = frogr_controller_get_active_account (priv->controller);
 
-  gtk_dialog_set_response_sensitive (GTK_DIALOG (progress_dialog),
-                                     GTK_RESPONSE_CANCEL, TRUE);
+  if (!FROGR_IS_ACCOUNT (account) || !frogr_account_has_extra_info (account))
+    return g_strdup (_("Not connected to Flickr"));
 
-  gtk_container_set_border_width (GTK_CONTAINER (progress_dialog), 6);
-  gtk_window_set_default_size (GTK_WINDOW (progress_dialog), 250, -1);
+  /* Just use the username here ant not the fullname (when available),
+     since it could happen that the full name was the same for two
+     different usernames from the same frogr user, thus making
+     impossible to distinguish which account you are using. */
+  login = frogr_account_get_username (account);
+  is_pro = frogr_account_is_pro (account);
 
-  progress_vbox = gtk_dialog_get_content_area (GTK_DIALOG (progress_dialog));
+  /* Login string, showing the user is PRO (second '%s') if so. */
+  login_str = g_strdup_printf (_("Connected as %s%s"), login,
+                               (is_pro ? _(" (PRO account)") : ""));
 
-  progress_label = gtk_label_new (NULL);
-  gtk_box_pack_start (GTK_BOX (progress_vbox), progress_label, FALSE, FALSE, 6);
+  /* Pro users do not have any limit of quota, so it makes no sense to
+     permanently show that they have 2.0 GB / 2.0 GB remaining */
+  if (!is_pro)
+    {
+      gchar *remaining_bw_str = NULL;
+      gchar *max_bw_str = NULL;
+      gulong remaining_bw;
+      gulong max_bw;
 
-  progress_bar = gtk_progress_bar_new ();
-  gtk_progress_bar_set_show_text (GTK_PROGRESS_BAR (progress_bar), TRUE);
+      remaining_bw = frogr_account_get_remaining_bandwidth (account);
+      max_bw = frogr_account_get_max_bandwidth (account);
 
-  gtk_box_pack_start (GTK_BOX (progress_vbox), progress_bar, FALSE, FALSE, 6);
+      remaining_bw_str = frogr_util_get_datasize_string (remaining_bw);
+      max_bw_str = frogr_util_get_datasize_string (max_bw);
 
-  gtk_widget_hide (progress_dialog);
-  priv->progress_dialog = progress_dialog;
-  priv->progress_bar = progress_bar;
-  priv->progress_label = progress_label;
-  priv->progress_is_showing = FALSE;
-  priv->state_description = NULL;
+      if (remaining_bw_str && max_bw_str)
+        {
+          /* Will show in the status bar the amount of data (in KB, MB
+             or GB) the user is currently allowed to upload to flicker
+             till the end of the month, in a CURRENT / MAX fashion.
+             The '-' at the beginning is just a separator, since more
+             blocks of text will be shown in the status bar too. */
+          bandwidth_str = g_strdup_printf (_(" - %s / %s remaining"),
+                                           remaining_bw_str,
+                                           max_bw_str);
+        }
 
-  /* Initialize model */
-  priv->tree_model = GTK_TREE_MODEL (gtk_list_store_new (3,
-                                                         G_TYPE_STRING,
-                                                         GDK_TYPE_PIXBUF,
-                                                         G_TYPE_POINTER));
-  gtk_icon_view_set_model (GTK_ICON_VIEW (icon_view), priv->tree_model);
-  gtk_icon_view_set_pixbuf_column (GTK_ICON_VIEW (icon_view), PIXBUF_COL);
-  gtk_icon_view_set_selection_mode (GTK_ICON_VIEW (icon_view),
-                                    GTK_SELECTION_MULTIPLE);
-  gtk_icon_view_set_columns (GTK_ICON_VIEW (icon_view), -1);
-  gtk_icon_view_set_item_width (GTK_ICON_VIEW (icon_view), IV_THUMB_WIDTH + IV_THUMB_PADDING);
-  gtk_icon_view_set_item_padding (GTK_ICON_VIEW (icon_view), IV_THUMB_PADDING);
-  gtk_icon_view_set_column_spacing (GTK_ICON_VIEW (icon_view), IV_THUMB_PADDING);
-  gtk_icon_view_set_row_spacing (GTK_ICON_VIEW (icon_view), IV_THUMB_PADDING);
-  gtk_widget_set_has_tooltip (icon_view, TRUE);
+      g_free (remaining_bw_str);
+      g_free (max_bw_str);
+    }
 
-  gtk_window_set_default_size (priv->window, MINIMUM_WINDOW_WIDTH, MINIMUM_WINDOW_HEIGHT);
+  /* Check size of the loaded pictures, if any */
+  pictures = frogr_model_get_pictures (priv->model);
+  n_pictures = frogr_model_n_pictures (priv->model);
+  if (n_pictures)
+    {
+      GSList *item = NULL;
+      gulong total_size = 0;
+      gchar *total_size_str = NULL;
 
-  /* Init status bar */
-  priv->sb_context_id =
-    gtk_statusbar_get_context_id (GTK_STATUSBAR (priv->status_bar),
-                                  "Status bar messages");
+      for (item = pictures; item; item = g_slist_next (item))
+        total_size += frogr_picture_get_filesize (FROGR_PICTURE (item->data));
 
-  /* Connect signals */
-  g_signal_connect (G_OBJECT (priv->window), "delete-event",
-                    G_CALLBACK (_on_main_view_delete_event),
-                    self);
+      total_size_str = frogr_util_get_datasize_string (total_size);
 
-  g_signal_connect (G_OBJECT (priv->icon_view), "query-tooltip",
-                    G_CALLBACK (_on_icon_view_query_tooltip),
-                    self);
+      /* Will show in the status bar the amount of pictures and data
+         (in KB, MB or GB) that would be uploaded as the sum of the
+         sizes for every picture loaded in the application */
+      upload_size_str = g_strdup_printf (ngettext (" - %d file to upload (%s)",
+                                                   " - %d files to upload (%s)",
+                                                   n_pictures),
+                                         n_pictures, total_size_str);
+      g_free (total_size_str);
+    }
 
-  g_signal_connect (G_OBJECT (priv->icon_view), "selection-changed",
-                    G_CALLBACK (_on_icon_view_selection_changed),
-                    self);
+  /* Build the final string */
+  description = g_strdup_printf ("%s%s%s",
+                                 login_str,
+                                 (bandwidth_str ? bandwidth_str : ""),
+                                 (upload_size_str ? upload_size_str : ""));
+  g_free (login_str);
+  g_free (bandwidth_str);
+  g_free (upload_size_str);
 
-  g_signal_connect (G_OBJECT (priv->progress_dialog), "response",
-                    G_CALLBACK(_progress_dialog_response),
-                    self);
+  return description;
+}
 
-  g_signal_connect (G_OBJECT (priv->progress_dialog),
-                    "delete-event",
-                    G_CALLBACK(_progress_dialog_delete_event),
-                    self);
+static void
+_update_sensitiveness (FrogrMainView *self)
+{
+  FrogrMainViewPrivate *priv = FROGR_MAIN_VIEW_GET_PRIVATE (self);
+  /* gboolean has_accounts = FALSE; */
+  gboolean has_pics = FALSE;
+  gint n_selected_pics = 0;
 
-  gtk_builder_connect_signals (builder, self);
+  /* Set sensitiveness */
+  switch (frogr_controller_get_state (priv->controller))
+    {
+    case FROGR_STATE_LOADING_PICTURES:
+    case FROGR_STATE_UPLOADING_PICTURES:
+      gtk_action_set_sensitive (priv->load_project_action, FALSE);
+      gtk_action_set_sensitive (priv->save_project_action, FALSE);
+      gtk_action_set_sensitive (priv->save_project_as_action, FALSE);
+      gtk_action_set_sensitive (priv->load_pictures_action, FALSE);
+      gtk_action_set_sensitive (priv->remove_pictures_action, FALSE);
+      gtk_action_set_sensitive (priv->upload_pictures_action, FALSE);
+      gtk_action_set_sensitive (priv->open_in_external_viewer_action, FALSE);
+      gtk_action_set_sensitive (priv->add_tags_action, FALSE);
+      gtk_action_set_sensitive (priv->edit_details_action, FALSE);
+      gtk_action_set_sensitive (priv->add_to_group_action, FALSE);
+      gtk_action_set_sensitive (priv->add_to_set_action, FALSE);
+      gtk_action_set_sensitive (priv->add_to_new_set_action, FALSE);
+      /* gtk_widget_set_sensitive (priv->accounts_menu_item, FALSE); */
+      gtk_widget_set_sensitive (priv->add_to_set_menu_item, FALSE);
+      break;
 
-  /* Show the UI */
-  gtk_widget_show_all (GTK_WIDGET(priv->window));
+    case FROGR_STATE_IDLE:
+      has_pics = (_n_pictures (self) > 0);
+      /* has_accounts = (priv->accounts_menu != NULL); */
+      n_selected_pics = priv->n_selected_pictures;
 
-#ifdef MAC_INTEGRATION
-  _tweak_menu_bar_for_mac (self);
-#endif
+      gtk_action_set_sensitive (priv->load_project_action, TRUE);
+      gtk_action_set_sensitive (priv->save_project_action, TRUE);
+      gtk_action_set_sensitive (priv->save_project_as_action, TRUE);
+      gtk_action_set_sensitive (priv->load_pictures_action, TRUE);
+      /* gtk_widget_set_sensitive (priv->accounts_menu_item, has_accounts); */
+      gtk_action_set_sensitive (priv->upload_pictures_action, has_pics);
+      gtk_action_set_sensitive (priv->remove_pictures_action, n_selected_pics > 0);
+      gtk_action_set_sensitive (priv->open_in_external_viewer_action, n_selected_pics > 0);
+      gtk_action_set_sensitive (priv->add_tags_action, n_selected_pics > 0);
+      gtk_action_set_sensitive (priv->edit_details_action, n_selected_pics > 0);
+      gtk_action_set_sensitive (priv->add_to_group_action, n_selected_pics > 0);
+      gtk_action_set_sensitive (priv->add_to_set_action, n_selected_pics > 0);
+      gtk_action_set_sensitive (priv->add_to_new_set_action, n_selected_pics > 0);
+      gtk_widget_set_sensitive (priv->add_to_set_menu_item, n_selected_pics > 0);
+      break;
 
-  /* Update window title */
-  _update_window_title (self, FALSE);
+    default:
+      g_warning ("Invalid state reached!!");
+    }
+}
 
-  /* Update UI */
-  _update_ui (FROGR_MAIN_VIEW (self));
+static void
+_update_ui (FrogrMainView *self)
+{
+  FrogrMainViewPrivate *priv = FROGR_MAIN_VIEW_GET_PRIVATE (self);
 
-  /* Show the auth dialog, if needed, on idle */
-  g_idle_add ((GSourceFunc) _maybe_show_auth_dialog_on_idle, self);
+  /* Set sensitiveness */
+  _update_sensitiveness (self);
 
-  return object;
+  /* Update status bar from model's state description */
+  if (frogr_controller_get_state (priv->controller) == FROGR_STATE_IDLE)
+    frogr_main_view_set_status_text (self, priv->state_description);
 }
 
 static void
@@ -2243,7 +2210,6 @@ _frogr_main_view_finalize (GObject *object)
   g_free (priv->project_dir);
   g_free (priv->project_filepath);
   g_free (priv->state_description);
-  gtk_widget_destroy (GTK_WIDGET (priv->window));
 
   G_OBJECT_CLASS(frogr_main_view_parent_class)->finalize (object);
 }
@@ -2253,20 +2219,9 @@ frogr_main_view_class_init (FrogrMainViewClass *klass)
 {
   GObjectClass *obj_class = (GObjectClass *)klass;
 
-  obj_class->set_property = _frogr_main_view_set_property;
-  obj_class->get_property = _frogr_main_view_get_property;
-  obj_class->constructor = _frogr_main_view_constructor;
   obj_class->dispose = _frogr_main_view_dispose;
   obj_class->finalize = _frogr_main_view_finalize;
 
-  g_object_class_install_property (obj_class,
-                                   PROP_GTK_APPLICATION,
-                                   g_param_spec_object ("gtk-application",
-                                                        "gtk-application",
-                                                        "GtkApplication associated to the main view",
-                                                        GTK_TYPE_APPLICATION,
-                                                        G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
-
   g_type_class_add_private (obj_class, sizeof (FrogrMainViewPrivate));
 }
 
@@ -2274,46 +2229,13 @@ static void
 frogr_main_view_init (FrogrMainView *self)
 {
   FrogrMainViewPrivate *priv = NULL;
-  const gchar *icons_path = NULL;
-  gchar *full_path = NULL;
-  GList *icons = NULL;
 
-  /* Init model, controller and configuration */
+  /* Initialize internal state NOT related with the UI */
   priv = FROGR_MAIN_VIEW_GET_PRIVATE (self);
   priv->model = frogr_model_new ();
   priv->controller = g_object_ref (frogr_controller_get_instance ());
   priv->config = g_object_ref (frogr_config_get_instance ());
 
-  /* Provide a default icon list in several sizes */
-  icons_path = frogr_util_get_icons_dir ();
-  full_path = g_strdup_printf ("%s/" MAIN_VIEW_ICON("128x128"), icons_path);
-  icons = g_list_prepend (icons, gdk_pixbuf_new_from_file (full_path, NULL));
-  g_free (full_path);
-
-  full_path = g_strdup_printf ("%s/" MAIN_VIEW_ICON("64x64"), icons_path);
-  icons = g_list_prepend (icons, gdk_pixbuf_new_from_file (full_path, NULL));
-  g_free (full_path);
-
-  full_path = g_strdup_printf ("%s/" MAIN_VIEW_ICON("48x48"), icons_path);
-  icons = g_list_prepend (icons, gdk_pixbuf_new_from_file (full_path, NULL));
-  g_free (full_path);
-
-  full_path = g_strdup_printf ("%s/" MAIN_VIEW_ICON("32x32"), icons_path);
-  icons = g_list_prepend (icons, gdk_pixbuf_new_from_file (full_path, NULL));
-  g_free (full_path);
-
-  full_path = g_strdup_printf ("%s/" MAIN_VIEW_ICON("24x24"), icons_path);
-  icons = g_list_prepend (icons, gdk_pixbuf_new_from_file (full_path, NULL));
-  g_free (full_path);
-
-  full_path = g_strdup_printf ("%s/" MAIN_VIEW_ICON("16x16"), icons_path);
-  icons = g_list_prepend (icons, gdk_pixbuf_new_from_file (full_path, NULL));
-  g_free (full_path);
-
-  gtk_window_set_default_icon_list (icons);
-  g_list_foreach (icons, (GFunc) g_object_unref, NULL);
-  g_list_free (icons);
-
   /* Initialize sorting criteria and reverse */
   priv->sorted_pictures = NULL;
   priv->sorting_criteria = frogr_config_get_mainview_sorting_criteria (priv->config);
@@ -2354,21 +2276,15 @@ frogr_main_view_init (FrogrMainView *self)
 FrogrMainView *
 frogr_main_view_new (GtkApplication *app)
 {
-  GObject *new = g_object_new (FROGR_TYPE_MAIN_VIEW,
-                               "gtk-application", app,
-                               NULL);
-  return FROGR_MAIN_VIEW (new);
-}
-
-GtkWindow *
-frogr_main_view_get_window (FrogrMainView *self)
-{
-  FrogrMainViewPrivate *priv = NULL;
-
-  g_return_val_if_fail(FROGR_IS_MAIN_VIEW (self), NULL);
+  FrogrMainView *mainview =
+    FROGR_MAIN_VIEW (g_object_new (FROGR_TYPE_MAIN_VIEW,
+                                   "application", app,
+                                   "show-menubar", TRUE,
+                                   NULL));
 
-  priv = FROGR_MAIN_VIEW_GET_PRIVATE (self);
-  return priv->window;
+  /* Now initialize all the stuff strictly related to the UI */
+  _initialize_ui (mainview);
+  return mainview;
 }
 
 void
diff --git a/src/frogr-main-view.h b/src/frogr-main-view.h
index 686b62f..7d4e85a 100644
--- a/src/frogr-main-view.h
+++ b/src/frogr-main-view.h
@@ -39,20 +39,18 @@ typedef struct _FrogrMainViewClass   FrogrMainViewClass;
 
 struct _FrogrMainViewClass
 {
-  GObjectClass parent_class;
+  GtkApplicationWindowClass parent_class;
 };
 
 struct _FrogrMainView
 {
-  GObject parent;
+  GtkApplicationWindow parent;
 };
 
 GType frogr_main_view_get_type (void) G_GNUC_CONST;
 
 FrogrMainView *frogr_main_view_new (GtkApplication *app);
 
-GtkWindow *frogr_main_view_get_window (FrogrMainView *self);
-
 void frogr_main_view_set_status_text (FrogrMainView *self, const gchar *text);
 
 void frogr_main_view_show_progress (FrogrMainView *self, const gchar *text);



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]