[aisleriot] Add recent items to the game select dialog



commit 3439a2e1285796b95a7e1aa066f52375c74a4404
Author: William Jon McCann <william jon mccann gmail com>
Date:   Mon Mar 4 01:16:49 2013 -0500

    Add recent items to the game select dialog
    
    https://bugzilla.gnome.org/show_bug.cgi?id=695102

 src/ar-game-chooser.c |  206 ++++++++++++++++++++++++++++++++++++++++--------
 1 files changed, 171 insertions(+), 35 deletions(-)
---
diff --git a/src/ar-game-chooser.c b/src/ar-game-chooser.c
index 240309f..686fb53 100644
--- a/src/ar-game-chooser.c
+++ b/src/ar-game-chooser.c
@@ -26,6 +26,7 @@
 #include "ar-debug.h"
 #include "ar-runtime.h"
 #include "ar-string-utils.h"
+#include "conf.h"
 
 struct _ArGameChooser
 {
@@ -54,12 +55,14 @@ enum {
 
 enum {
   COL_NAME,
-  COL_GAME_MODULE
+  COL_GAME_MODULE,
+  COL_RECENT_RANK
 };
 
 #define SELECT_GAME_DIALOG_MIN_WIDTH (300)
 #define SELECT_GAME_DIALOG_MIN_HEIGHT (256)
 #define SELECTED_PATH_DATA_KEY "selected-path"
+#define MAX_RECENT 5
 
 /* private functions */
 
@@ -111,6 +114,154 @@ ar_game_chooser_init (ArGameChooser *chooser)
   chooser->priv = G_TYPE_INSTANCE_GET_PRIVATE (chooser, AR_TYPE_GAME_CHOOSER, ArGameChooserPrivate);
 }
 
+static void
+store_add_module (GtkListStore *store,
+                  const char *game_module,
+                  int recent_rank)
+{
+  char *game_name;
+  GtkTreeIter iter;
+
+  game_name = ar_filename_to_display_name (game_module);
+  gtk_list_store_insert_with_values (store, &iter,
+                                     -1,
+                                     COL_NAME, game_name,
+                                     COL_GAME_MODULE, game_module,
+                                     COL_RECENT_RANK, recent_rank,
+                                     -1);
+  g_free (game_name);
+}
+
+static void
+store_add_separator (GtkListStore *store)
+{
+  GtkTreeIter iter;
+
+  gtk_list_store_insert_with_values (store, &iter,
+                                     -1,
+                                     COL_NAME, NULL,
+                                     COL_GAME_MODULE, NULL,
+                                     COL_RECENT_RANK, 0,
+                                     -1);
+}
+
+static gboolean
+row_separator_func (GtkTreeModel *model,
+                    GtkTreeIter *iter,
+                    gpointer data)
+{
+  gboolean ret;
+  char *game_module;
+
+  gtk_tree_model_get (model, iter,
+                      COL_GAME_MODULE, &game_module,
+                      -1);
+  ret = (game_module == NULL);
+  g_free (game_module);
+
+  return ret;
+}
+
+static char **
+get_unique_recent_modules (const char *current_module)
+{
+  char **recent_games, **new_recent;
+  gsize i;
+  gsize n_recent = 0;
+  gsize n_new_recent = 0;
+
+  recent_games = ar_conf_get_string_list (NULL, aisleriot_conf_get_key (CONF_RECENT_GAMES), &n_recent, NULL);
+
+  if (recent_games == NULL) {
+    new_recent = g_new (char *, 2);
+    new_recent[0] = g_strdup (current_module);
+    new_recent[1] = NULL;
+    n_new_recent = 1;
+  } else {
+    new_recent = g_new (char *, MIN (n_recent + 1, MAX_RECENT) + 1);
+    n_new_recent = 0;
+
+    new_recent[n_new_recent++] = g_strdup (current_module);
+
+    for (i = 0; i < n_recent && n_new_recent < MAX_RECENT; ++i) {
+      const char *module = recent_games[i];
+      gboolean found = FALSE;
+      gsize j;
+
+      for (j = 0; j < n_new_recent; j++) {
+        const char *existing = new_recent[j];
+
+        if (g_ascii_strcasecmp (existing, module) == 0) {
+          found = TRUE;
+          break;
+        }
+      }
+
+      if (!found)
+        new_recent[n_new_recent++] = g_strdup (recent_games[i]);
+    }
+
+    /* NULL termination */
+    new_recent[n_new_recent] = NULL;
+
+    g_strfreev (recent_games);
+  }
+
+  return new_recent;
+}
+
+static void
+add_recent_items (ArGameChooser *chooser)
+{
+  ArGameChooserPrivate *priv = chooser->priv;
+  const char *current_game_module;
+  char **games;
+  int i;
+
+  current_game_module = aisleriot_window_get_game_module (priv->window);
+  games = get_unique_recent_modules (current_game_module);
+
+  for (i = 0; games[i] != NULL; ++i) {
+    store_add_module (priv->store, games[i], MAX_RECENT - i);
+  }
+
+  g_strfreev (games);
+
+  store_add_separator (priv->store);
+}
+
+static int
+sort_func (GtkTreeModel *model,
+           GtkTreeIter *iter_a,
+           GtkTreeIter *iter_b,
+           gpointer data)
+{
+  int ret;
+  int recent_a, recent_b;
+  char *name_a, *name_b;
+
+  gtk_tree_model_get (model, iter_a,
+                      COL_NAME, &name_a,
+                      COL_RECENT_RANK, &recent_a,
+                      -1);
+  gtk_tree_model_get (model, iter_b,
+                      COL_NAME, &name_b,
+                      COL_RECENT_RANK, &recent_b,
+                      -1);
+  if (recent_a < recent_b) {
+    ret = 1;
+  } else if (recent_a > recent_b) {
+    ret = -1;
+  } else {
+    ret = g_utf8_collate (name_a, name_b);
+  }
+
+  g_free (name_a);
+  g_free (name_b);
+
+  return ret;
+}
+
 static GObject *
 ar_game_chooser_constructor (GType type,
                              guint n_construct_properties,
@@ -127,9 +278,7 @@ ar_game_chooser_constructor (GType type,
   GtkTreeViewColumn *column;
   GtkCellRenderer *renderer;
   GtkWidget *hbox;
-  GtkTreeIter current_iter;
-  gboolean current_iter_set = FALSE;
-  const char *current_game_module;
+  GtkTreePath *path;
   char **games;
   int i;
   GtkWidget *content_area;
@@ -144,32 +293,16 @@ ar_game_chooser_constructor (GType type,
 
   g_assert (priv->window != NULL);
 
-  priv->store = list = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_STRING);
+  priv->store = list = gtk_list_store_new (3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INT);
 
-  current_game_module = aisleriot_window_get_game_module (priv->window);
+  add_recent_items (chooser);
 
   games = ar_get_game_modules ();
   if (games != NULL) {
     for (i = 0; games[i]; ++i) {
       const char *game_module = games[i];
-      char *game_name;
-      GtkTreeIter iter;
-
-      game_name = ar_filename_to_display_name (game_module);
 
-      gtk_list_store_insert_with_values (GTK_LIST_STORE (list), &iter,
-                                         -1,
-                                         COL_NAME, game_name,
-                                         COL_GAME_MODULE, game_module,
-                                         -1);
-
-      if (current_game_module &&
-          strcmp (current_game_module, game_module) == 0) {
-        current_iter = iter;
-        current_iter_set = TRUE;
-      }
-
-      g_free (game_name);
+      store_add_module (priv->store, game_module, -1);
     }
   }
 
@@ -204,8 +337,15 @@ ar_game_chooser_constructor (GType type,
   g_signal_connect (list_view, "row-activated",
                     G_CALLBACK (row_activated_cb), chooser);
 
+  gtk_tree_view_set_row_separator_func (GTK_TREE_VIEW (list_view),
+                                        (GtkTreeViewRowSeparatorFunc)row_separator_func,
+                                        NULL, NULL);
+
   gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (list),
                                         0, GTK_SORT_ASCENDING);
+  gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (list),
+                                   0, (GtkTreeIterCompareFunc)sort_func,
+                                   NULL, NULL);
 
   hbox = gtk_hbox_new (FALSE, 12);
   gtk_container_set_border_width (GTK_CONTAINER (hbox), 5);
@@ -248,18 +388,14 @@ ar_game_chooser_constructor (GType type,
   /* Select the row corresponding to the currently loaded game,
    * and scroll to it.
    */
-  if (current_iter_set) {
-    GtkTreePath *path;
-
-    gtk_tree_selection_select_iter (selection, &current_iter);
-
-    /* Scroll view to the current item */
-    path = gtk_tree_model_get_path (GTK_TREE_MODEL (list), &current_iter);
-    gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (list_view), path, NULL,
-                                  TRUE,
-                                 0.5, 0.0);
-    gtk_tree_path_free (path);
-  }
+  path = gtk_tree_path_new_first ();
+  gtk_tree_selection_select_path (selection, path);
+
+  /* Scroll view to the current item */
+  gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (list_view), path, NULL,
+                                TRUE,
+                                0.5, 0.0);
+  gtk_tree_path_free (path);
 
   return object;
 }


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