[Xfce4-commits] <xfce4-appfinder:master> Make a start with dbus, model sharing and settings.

Nick Schermer noreply at xfce.org
Sat Jul 9 16:18:04 CEST 2011


Updating branch refs/heads/master
         to a36909d9ad7e1369b1057f5f87296151f8aa653e (commit)
       from 126620ac040126189b2e12dabd47725bb05f70e2 (commit)

commit a36909d9ad7e1369b1057f5f87296151f8aa653e
Author: Nick Schermer <nick at xfce.org>
Date:   Sat Jun 11 21:42:30 2011 +0200

    Make a start with dbus, model sharing and settings.
    
    Couple of things are not working yet.
    - Keep the model around if there are no windows.
    - Categories are not working in the 2nd window.
    - Forking to the background.
    - envp + screen over dbus.

 configure.ac.in                 |    2 +
 data/xfce4-appfinder.desktop.in |    2 +-
 src/Makefile.am                 |    7 +-
 src/appfinder-category-model.c  |    1 +
 src/appfinder-model.c           |  187 ++++++-----------------
 src/appfinder-model.h           |   31 +---
 src/appfinder-private.h         |   39 +++++
 src/appfinder-window.c          |  160 ++++++++++++++------
 src/appfinder-window.h          |    2 -
 src/main.c                      |  319 +++++++++++++++++++++++++++++++++++++--
 10 files changed, 519 insertions(+), 231 deletions(-)

diff --git a/configure.ac.in b/configure.ac.in
index c65d770..62fd416 100644
--- a/configure.ac.in
+++ b/configure.ac.in
@@ -76,6 +76,8 @@ XDT_CHECK_PACKAGE([GTK], [gtk+-2.0], [2.20.0])
 XDT_CHECK_PACKAGE([LIBXFCE4UTIL], [libxfce4util-1.0], [4.8.0])
 XDT_CHECK_PACKAGE([LIBXFCE4UI], [libxfce4ui-1], [4.8.0])
 XDT_CHECK_PACKAGE([GARCON], [garcon-1], [0.1.7])
+XDT_CHECK_PACKAGE([DBUS_GLIB], [dbus-glib-1], [0.84])
+XDT_CHECK_PACKAGE([XFCONF], [libxfconf-0], [4.8.0])
 
 dnl ***********************************
 dnl *** Check for debugging support ***
diff --git a/data/xfce4-appfinder.desktop.in b/data/xfce4-appfinder.desktop.in
index f79f952..2b4bec2 100644
--- a/data/xfce4-appfinder.desktop.in
+++ b/data/xfce4-appfinder.desktop.in
@@ -1,6 +1,6 @@
 [Desktop Entry]
 Version=1.0
-Exec=xfce4-appfinder --finder
+Exec=xfce4-appfinder -x
 Icon=gtk-find
 StartupNotify=true
 Terminal=false
diff --git a/src/Makefile.am b/src/Makefile.am
index b32ae27..2c6551f 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -13,16 +13,20 @@ xfce4_appfinder_SOURCES = \
 	appfinder-category-model.h \
 	appfinder-model.c \
 	appfinder-model.h \
+	appfinder-private.h \
 	appfinder-window.c \
 	appfinder-window.h \
 	main.c
 
 xfce4_appfinder_CFLAGS = \
+	$(GLIB_CFLAGS) \
 	$(GTHREAD_CFLAGS) \
 	$(GTK_CFLAGS) \
 	$(LIBXFCE4UTIL_CFLAGS) \
 	$(LIBXFCE4UI_CFLAGS) \
 	$(GARCON_CFLAGS) \
+	$(DBUS_GLIB_CFLAGS) \
+	$(XFCONF_CFLAGS) \
 	$(PLATFORM_CFLAGS)
 
 xfce4_appfinder_LDADD = \
@@ -32,7 +36,8 @@ xfce4_appfinder_LDADD = \
 	$(LIBXFCE4UTIL_LIBS) \
 	$(LIBXFCE4UI_LIBS) \
 	$(GARCON_LIBS) \
-	$(LIBXFCONF_LIBS)
+	$(XFCONF_LIBS) \
+	$(DBUS_GLIB_LIBS)
 
 xfce4_appfinder_LDFLAGS = \
 	-no-undefined \
diff --git a/src/appfinder-category-model.c b/src/appfinder-category-model.c
index 957029f..06d208d 100644
--- a/src/appfinder-category-model.c
+++ b/src/appfinder-category-model.c
@@ -29,6 +29,7 @@
 
 #include <src/appfinder-model.h>
 #include <src/appfinder-category-model.h>
+#include <src/appfinder-private.h>
 
 
 
diff --git a/src/appfinder-model.c b/src/appfinder-model.c
index 0d49e5c..470eec0 100644
--- a/src/appfinder-model.c
+++ b/src/appfinder-model.c
@@ -29,6 +29,7 @@
 #include <garcon/garcon.h>
 
 #include <src/appfinder-model.h>
+#include <src/appfinder-private.h>
 
 
 
@@ -89,10 +90,6 @@ struct _XfceAppfinderModel
   GdkPixbuf         *command_icon_small;
   GdkPixbuf         *command_icon_large;
 
-  gchar             *filter_category;
-  gchar             *filter_string;
-  guint              filter_idle_id;
-
   guint              collect_idle_id;
   GSList            *collect_items;
   GSList            *collect_categories;
@@ -109,7 +106,6 @@ typedef struct
   GPtrArray      *categories;
   gchar          *command;
   gchar          *tooltip;
-  guint           visible : 1;
 
   GdkPixbuf      *icon_small;
   GdkPixbuf      *icon_large;
@@ -124,7 +120,7 @@ enum
 
 
 
-static guint    model_signals[LAST_SIGNAL];
+static guint model_signals[LAST_SIGNAL];
 
 
 
@@ -207,16 +203,10 @@ xfce_appfinder_model_finalize (GObject *object)
   g_slist_foreach (model->items, (GFunc) xfce_appfinder_model_item_free, NULL);
   g_slist_free (model->items);
 
-  if (G_UNLIKELY (model->filter_idle_id != 0))
-    g_source_remove (model->filter_idle_id);
-
   if (model->collect_desktop_ids != NULL)
     g_hash_table_destroy (model->collect_desktop_ids);
   g_hash_table_destroy (model->items_hash);
 
-  g_free (model->filter_category);
-  g_free (model->filter_string);
-
   g_object_unref (G_OBJECT (model->command_icon_large));
   g_object_unref (G_OBJECT (model->command_icon_small));
 
@@ -259,9 +249,6 @@ xfce_appfinder_model_get_column_type (GtkTreeModel *tree_model,
     case XFCE_APPFINDER_MODEL_COLUMN_ICON_LARGE:
       return GDK_TYPE_PIXBUF;
 
-    case XFCE_APPFINDER_MODEL_COLUMN_VISIBLE:
-      return G_TYPE_BOOLEAN;
-
     default:
       g_assert_not_reached ();
       return G_TYPE_INVALID;
@@ -334,11 +321,6 @@ xfce_appfinder_model_get_value (GtkTreeModel *tree_model,
 
   switch (column)
     {
-    case XFCE_APPFINDER_MODEL_COLUMN_VISIBLE:
-      g_value_init (value, G_TYPE_BOOLEAN);
-      g_value_set_boolean (value, item->visible);
-      break;
-
     case XFCE_APPFINDER_MODEL_COLUMN_ABSTRACT:
       if (item->abstract == NULL)
         {
@@ -704,7 +686,6 @@ xfce_appfinder_model_item_new (GarconMenuItem *menu_item)
 
   item = g_slice_new0 (ModelItem);
   item->item = g_object_ref (G_OBJECT (menu_item));
-  item->visible = TRUE;
 
   command = garcon_menu_item_get_command (menu_item);
   if (G_LIKELY (command != NULL))
@@ -983,153 +964,77 @@ xfce_appfinder_model_collect_thread (gpointer user_data)
 
 
 
-static gboolean
-xfce_appfinder_model_filter_idle (gpointer data)
+XfceAppfinderModel *
+xfce_appfinder_model_get (void)
 {
-  XfceAppfinderModel *model = XFCE_APPFINDER_MODEL (data);
-  GSList             *li;
-  gint                idx;
-  ModelItem          *item;
-  gboolean            visible;
-  GtkTreeIter         iter;
-  GtkTreePath        *path;
+  static XfceAppfinderModel *model = NULL;
 
-  GDK_THREADS_ENTER ();
-
-  for (li = model->items, idx = 0; li != NULL; li = li->next, idx++)
+  if (G_LIKELY (model != NULL))
     {
-      item = li->data;
-      visible = TRUE;
-
-      g_return_val_if_fail ((item->item == NULL && item->command != NULL)
-                            || (item->item != NULL && GARCON_IS_MENU_ITEM (item->item)), FALSE);
-
-      if (item->item != NULL)
-        {
-          if (model->filter_category != NULL
-              && !xfce_appfinder_model_ptr_array_strcmp (item->categories, model->filter_category))
-            {
-              visible = FALSE;
-            }
-          else if (model->filter_string != NULL)
-            {
-              if (item->key == NULL)
-                item->key = xfce_appfinder_model_item_key (item->item);
-
-              visible = strstr (item->key, model->filter_string) != NULL;
-            }
-        }
-      else if (item->command != NULL)
-        {
-          if (model->filter_category == NULL
-              || *model->filter_category != '\0')
-            {
-              visible = FALSE;
-            }
-          else if (model->filter_string != NULL)
-            {
-              visible = strstr (item->command, model->filter_string) != NULL;
-            }
-        }
-
-      if (item->visible != visible)
-        {
-          item->visible = visible;
-
-          /* generate an iterator for the path */
-          ITER_INIT (iter, model->stamp, li);
-
-          /* tell the view that the volume has changed in some way */
-          path = gtk_tree_path_new_from_indices (idx, -1);
-          gtk_tree_model_row_changed (GTK_TREE_MODEL (model), path, &iter);
-          gtk_tree_path_free (path);
-        }
+      g_object_ref (G_OBJECT (model));
     }
-
-  GDK_THREADS_LEAVE ();
-
-  return FALSE;
-}
-
-
-
-static void
-xfce_appfinder_model_filter_idle_destroyed (gpointer data)
-{
-  XFCE_APPFINDER_MODEL (data)->filter_idle_id = 0;
-}
-
-
-
-static void
-xfce_appfinder_model_filter (XfceAppfinderModel *model)
-{
-  if (model->filter_idle_id == 0)
+  else
     {
-      model->filter_idle_id = g_idle_add_full (G_PRIORITY_HIGH_IDLE, xfce_appfinder_model_filter_idle,
-                                               model, xfce_appfinder_model_filter_idle_destroyed);
+      model = g_object_new (XFCE_TYPE_APPFINDER_MODEL, NULL);
+      g_message ("new model");
+      g_object_add_weak_pointer (G_OBJECT (model), (gpointer) &model);
     }
-}
-
 
-
-XfceAppfinderModel *
-xfce_appfinder_model_new (void)
-{
-  return g_object_new (XFCE_TYPE_APPFINDER_MODEL, NULL);
+  return model;
 }
 
 
 
-void
-xfce_appfinder_model_filter_category (XfceAppfinderModel *model,
-                                      const gchar        *category)
+gboolean
+xfce_appfinder_model_get_visible (XfceAppfinderModel *model,
+                                  const GtkTreeIter  *iter,
+                                  const gchar        *category,
+                                  const gchar        *string)
 {
-  g_return_if_fail (XFCE_IS_APPFINDER_MODEL (model));
-
-  if (g_strcmp0 (model->filter_category, category) == 0)
-    return;
-
-  g_free (model->filter_category);
-  model->filter_category = g_strdup (category);
-
-  xfce_appfinder_model_filter (model);
-}
-
+  ModelItem *item;
 
+  g_return_val_if_fail (XFCE_IS_APPFINDER_MODEL (model), FALSE);
+  g_return_val_if_fail (iter->stamp == model->stamp, FALSE);
 
-void
-xfce_appfinder_model_filter_string (XfceAppfinderModel *model,
-                                    const gchar        *seach_string)
-{
-  gchar *normalized;
+  item = ITER_GET_DATA (iter);
 
-  g_return_if_fail (XFCE_IS_APPFINDER_MODEL (model));
+  if (item->item != NULL)
+    {
+      g_return_val_if_fail (GARCON_IS_MENU_ITEM (item->item), FALSE);
 
-  if (model->filter_string == seach_string)
-    return;
+      if (category != NULL
+          && (*category == '\0'
+              || !xfce_appfinder_model_ptr_array_strcmp (item->categories, category)))
+        return FALSE;
 
-  g_free (model->filter_string);
+      if (string != NULL)
+        {
+          if (item->key == NULL)
+            item->key = xfce_appfinder_model_item_key (item->item);
 
-  if (IS_STRING (seach_string))
-    {
-      normalized = g_utf8_normalize (seach_string, -1, G_NORMALIZE_ALL);
-      model->filter_string = g_utf8_casefold (normalized, -1);
-      g_free (normalized);
+          return strstr (item->key, string) != NULL;
+        }
     }
-  else
+  else /* command item */
     {
-      model->filter_string = NULL;
+      g_return_val_if_fail (item->command != NULL, FALSE);
+
+      /* nul string will filter out the commands */
+      if (category == NULL || *category != '\0')
+        return FALSE;
+
+      if (string != NULL)
+        return strstr (item->command, string) != NULL;
     }
 
-  xfce_appfinder_model_filter (model);
+  return TRUE;
 }
 
 
 
 gboolean
 xfce_appfinder_model_execute (XfceAppfinderModel  *model,
-                              GtkTreeIter         *iter,
+                              const GtkTreeIter   *iter,
                               GdkScreen           *screen,
                               gboolean            *is_regular_command,
                               GError             **error)
@@ -1385,7 +1290,7 @@ xfce_appfinder_model_icon_theme_changed (XfceAppfinderModel *model)
           item->icon_large = g_object_ref (G_OBJECT (model->command_icon_large));
         }
 
-      if (item_changed && item->visible)
+      if (item_changed)
         {
           path = gtk_tree_path_new_from_indices (idx, -1);
           ITER_INIT (iter, model->stamp, li);
diff --git a/src/appfinder-model.h b/src/appfinder-model.h
index a4b018b..e192d0e 100644
--- a/src/appfinder-model.h
+++ b/src/appfinder-model.h
@@ -33,29 +33,11 @@ typedef struct _XfceAppfinderModel      XfceAppfinderModel;
 #define XFCE_IS_APPFINDER_MODEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), XFCE_TYPE_APPFINDER_MODEL))
 #define XFCE_APPFINDER_MODEL_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), XFCE_TYPE_APPFINDER_MODEL, XfceAppfinderModelClass))
 
-#define ICON_SMALL   32
-#define ICON_LARGE   48
-
-#define ITER_GET_DATA(iter)          (((GSList *) (iter)->user_data)->data)
-#define ITER_INIT(iter, iter_stamp, iter_data) \
-G_STMT_START { \
-  (iter).stamp = iter_stamp; \
-  (iter).user_data = iter_data; \
-} G_STMT_END
-#define IS_STRING(str) ((str) != NULL && *(str) != '\0')
-
-#ifdef DEBUG
-#define APPFINDER_DEBUG(...) g_print ("xfce4-appfinder: "); g_print (__VA_ARGS__); g_print ("\n")
-#else
-#define APPFINDER_DEBUG(...) G_STMT_START{ (void)0; }G_STMT_END
-#endif
-
 enum
 {
   XFCE_APPFINDER_MODEL_COLUMN_ABSTRACT,
   XFCE_APPFINDER_MODEL_COLUMN_ICON_SMALL,
   XFCE_APPFINDER_MODEL_COLUMN_ICON_LARGE,
-  XFCE_APPFINDER_MODEL_COLUMN_VISIBLE,
   XFCE_APPFINDER_MODEL_COLUMN_COMMAND,
   XFCE_APPFINDER_MODEL_COLUMN_URI,
   XFCE_APPFINDER_MODEL_COLUMN_TOOLTIP,
@@ -66,16 +48,15 @@ enum
 
 GType               xfce_appfinder_model_get_type             (void) G_GNUC_CONST;
 
-XfceAppfinderModel *xfce_appfinder_model_new                  (void) G_GNUC_MALLOC;
-
-void                xfce_appfinder_model_filter_category      (XfceAppfinderModel  *model,
-                                                               const gchar         *category);
+XfceAppfinderModel *xfce_appfinder_model_get                  (void) G_GNUC_MALLOC;
 
-void                xfce_appfinder_model_filter_string        (XfceAppfinderModel  *model,
-                                                               const gchar         *seach_string);
+gboolean            xfce_appfinder_model_get_visible          (XfceAppfinderModel  *model,
+                                                               const GtkTreeIter   *iter,
+                                                               const gchar         *category,
+                                                               const gchar         *string);
 
 gboolean            xfce_appfinder_model_execute              (XfceAppfinderModel  *model,
-                                                               GtkTreeIter         *iter,
+                                                               const GtkTreeIter   *iter,
                                                                GdkScreen           *screen,
                                                                gboolean            *is_regular_command,
                                                                GError             **error);
diff --git a/src/appfinder-private.h b/src/appfinder-private.h
new file mode 100644
index 0000000..43b2c73
--- /dev/null
+++ b/src/appfinder-private.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2011 Nick Schermer <nick at xfce.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef __XFCE_APPFINDER_PRIVATE_H__
+#define __XFCE_APPFINDER_PRIVATE_H__
+
+#define ICON_SMALL   32
+#define ICON_LARGE   48
+
+#define ITER_GET_DATA(iter)          (((GSList *) (iter)->user_data)->data)
+#define ITER_INIT(iter, iter_stamp, iter_data) \
+G_STMT_START { \
+  (iter).stamp = iter_stamp; \
+  (iter).user_data = iter_data; \
+} G_STMT_END
+#define IS_STRING(str) ((str) != NULL && *(str) != '\0')
+
+#ifdef DEBUG
+#define APPFINDER_DEBUG(...) g_print ("xfce4-appfinder: "); g_print (__VA_ARGS__); g_print ("\n")
+#else
+#define APPFINDER_DEBUG(...) G_STMT_START{ (void)0; }G_STMT_END
+#endif
+
+#endif /* !__XFCE_APPFINDER_PRIVATE_H__ */
diff --git a/src/appfinder-window.c b/src/appfinder-window.c
index 68dfbfd..eec8863 100644
--- a/src/appfinder-window.c
+++ b/src/appfinder-window.c
@@ -27,18 +27,25 @@
 #include <libxfce4util/libxfce4util.h>
 #include <libxfce4ui/libxfce4ui.h>
 #include <gdk/gdkkeysyms.h>
+#include <xfconf/xfconf.h>
 
 #include <src/appfinder-window.h>
 #include <src/appfinder-model.h>
 #include <src/appfinder-category-model.h>
+#include <src/appfinder-private.h>
+
+
+
+#define DEFAULT_WINDOW_WIDTH   400
+#define DEFAULT_WINDOW_HEIGHT  400
+#define DEFAULT_PANED_POSITION 180
 
 
 
 static void       xfce_appfinder_window_finalize                 (GObject                     *object);
+static void       xfce_appfinder_window_unmap                    (GtkWidget                   *widget);
 static gboolean   xfce_appfinder_window_key_press_event          (GtkWidget                   *widget,
                                                                   GdkEventKey                 *event);
-static gboolean   xfce_appfinder_window_delete_event             (GtkWidget                   *widget,
-                                                                  GdkEventAny                 *event);
 static void       xfce_appfinder_window_set_padding              (GtkWidget                   *entry,
                                                                   GtkWidget                   *align);
 static void       xfce_appfinder_window_entry_changed            (XfceAppfinderWindow         *window);
@@ -62,6 +69,9 @@ static void       xfce_appfinder_window_drag_data_get            (GtkWidget
                                                                   XfceAppfinderWindow         *window);
 static void       xfce_appfinder_window_category_changed         (GtkTreeSelection            *selection,
                                                                   XfceAppfinderWindow         *window);
+static gboolean   xfce_appfinder_window_item_visible             (GtkTreeModel                *model,
+                                                                  GtkTreeIter                 *iter,
+                                                                  gpointer                     data);
 static void       xfce_appfinder_window_item_changed             (XfceAppfinderWindow         *window);
 static void       xfce_appfinder_window_row_activated            (XfceAppfinderWindow         *window);
 static void       xfce_appfinder_window_icon_theme_changed       (XfceAppfinderWindow         *window);
@@ -78,25 +88,28 @@ struct _XfceAppfinderWindow
 {
   GtkWindow __parent__;
 
-  XfceAppfinderModel *model;
+  XfceAppfinderModel         *model;
 
   XfceAppfinderCategoryModel *category_model;
 
-  GtkEntryCompletion *completion;
+  GtkEntryCompletion         *completion;
+
+  GtkWidget                  *paned;
+  GtkWidget                  *entry;
+  GtkWidget                  *image;
+  GtkWidget                  *treeview;
 
-  GtkWidget *paned;
-  GtkWidget *entry;
-  GtkWidget *image;
-  GtkWidget *treeview;
+  GdkPixbuf                  *icon_find;
 
-  GdkPixbuf *icon_find;
+  GtkWidget                  *bbox;
+  GtkWidget                  *button_launch;
+  GtkWidget                  *bin_collapsed;
+  GtkWidget                  *bin_expanded;
 
-  GtkWidget *bbox;
-  GtkWidget *button_launch;
-  GtkWidget *bin_collapsed;
-  GtkWidget *bin_expanded;
+  gchar                      *filter_category;
+  gchar                      *filter_text;
 
-  gint       last_window_height;
+  gint                        last_window_height;
 };
 
 static const GtkTargetEntry target_list[] =
@@ -120,8 +133,8 @@ xfce_appfinder_window_class_init (XfceAppfinderWindowClass *klass)
   gobject_class->finalize = xfce_appfinder_window_finalize;
 
   gtkwidget_class = GTK_WIDGET_CLASS (klass);
+  gtkwidget_class->unmap = xfce_appfinder_window_unmap;
   gtkwidget_class->key_press_event = xfce_appfinder_window_key_press_event;
-  gtkwidget_class->delete_event = xfce_appfinder_window_delete_event;
 }
 
 
@@ -147,17 +160,21 @@ xfce_appfinder_window_init (XfceAppfinderWindow *window)
   GtkTreePath        *path;
   GtkEntryCompletion *completion;
   GtkIconTheme       *icon_theme;
+  XfconfChannel      *channel;
+  gint                integer;
 
-  window->last_window_height = 400;
+  channel = xfconf_channel_get ("xfce4-appfinder");
+  window->last_window_height = xfconf_channel_get_int (channel, "/LastWindowHeight", DEFAULT_WINDOW_HEIGHT);
 
-  window->model = xfce_appfinder_model_new ();
+  window->model = xfce_appfinder_model_get ();
   window->category_model = xfce_appfinder_category_model_new ();
   g_signal_connect_swapped (G_OBJECT (window->model), "categories-changed",
                             G_CALLBACK (xfce_appfinder_category_model_set_categories),
                             window->category_model);
 
   gtk_window_set_title (GTK_WINDOW (window), _("Application Finder"));
-  gtk_window_set_default_size (GTK_WINDOW (window), 500 /* todo, remember */, -1);
+  integer = xfconf_channel_get_int (channel, "/LastWindowWidth", DEFAULT_WINDOW_WIDTH);
+  gtk_window_set_default_size (GTK_WINDOW (window), integer, -1);
   gtk_window_set_icon_name (GTK_WINDOW (window), GTK_STOCK_EXECUTE);
 
   vbox = gtk_vbox_new (FALSE, 6);
@@ -209,7 +226,9 @@ xfce_appfinder_window_init (XfceAppfinderWindow *window)
 
   window->paned = pane = gtk_hpaned_new ();
   gtk_box_pack_start (GTK_BOX (vbox), pane, TRUE, TRUE, 0);
-  gtk_paned_set_position (GTK_PANED (pane), 180 /* todo remember */);
+  integer = xfconf_channel_get_int (channel, "/LastPanedPosition", DEFAULT_PANED_POSITION);
+  gtk_paned_set_position (GTK_PANED (pane), integer);
+  g_object_set (G_OBJECT (pane), "position-set", TRUE, NULL);
 
   scroll = gtk_scrolled_window_new (NULL, NULL);
   gtk_paned_add1 (GTK_PANED (pane), scroll);
@@ -257,7 +276,7 @@ xfce_appfinder_window_init (XfceAppfinderWindow *window)
   gtk_widget_show (scroll);
 
   filter_model = gtk_tree_model_filter_new (GTK_TREE_MODEL (window->model), NULL);
-  gtk_tree_model_filter_set_visible_column (GTK_TREE_MODEL_FILTER (filter_model), XFCE_APPFINDER_MODEL_COLUMN_VISIBLE);
+  gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model), xfce_appfinder_window_item_visible, window, NULL);
 
   window->treeview = treeview = gtk_tree_view_new_with_model (filter_model);
   gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE);
@@ -305,7 +324,7 @@ xfce_appfinder_window_init (XfceAppfinderWindow *window)
 
   button = gtk_button_new_from_stock (GTK_STOCK_CLOSE);
   gtk_container_add (GTK_CONTAINER (bbox), button);
-  g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (gtk_main_quit), NULL);
+  g_signal_connect_swapped (G_OBJECT (button), "clicked", G_CALLBACK (gtk_widget_destroy), window);
   gtk_widget_show (button);
 
   window->button_launch = button = gtk_button_new_with_mnemonic ("La_unch");
@@ -335,18 +354,44 @@ xfce_appfinder_window_finalize (GObject *object)
   g_object_unref (G_OBJECT (window->completion));
   g_object_unref (G_OBJECT (window->icon_find));
 
+  g_free (window->filter_category);
+  g_free (window->filter_text);
+
   (*G_OBJECT_CLASS (xfce_appfinder_window_parent_class)->finalize) (object);
 }
 
 
 
+static void
+xfce_appfinder_window_unmap (GtkWidget *widget)
+{
+  XfceAppfinderWindow *window = XFCE_APPFINDER_WINDOW (widget);
+  gint                 width, height;
+  gint                 position;
+  XfconfChannel       *channel;
+
+  position = gtk_paned_get_position (GTK_PANED (window->paned));
+  gtk_window_get_size (GTK_WINDOW (window), &width, &height);
+  if (!gtk_widget_get_visible (window->paned))
+    height = window->last_window_height;
+
+  (*GTK_WIDGET_CLASS (xfce_appfinder_window_parent_class)->unmap) (widget);
+
+  channel = xfconf_channel_get ("xfce4-appfinder");
+  xfconf_channel_set_int (channel, "/LastWindowHeight", height);
+  xfconf_channel_set_int (channel, "/LastWindowWidth", width);
+  xfconf_channel_set_int (channel, "/LastPanedPosition", position);
+}
+
+
+
 static gboolean
 xfce_appfinder_window_key_press_event (GtkWidget   *widget,
                                        GdkEventKey *event)
 {
   if (event->keyval == GDK_Escape)
     {
-      gtk_main_quit ();
+      gtk_widget_destroy (widget);
       return TRUE;
     }
 
@@ -355,17 +400,6 @@ xfce_appfinder_window_key_press_event (GtkWidget   *widget,
 
 
 
-static gboolean
-xfce_appfinder_window_delete_event (GtkWidget   *widget,
-                                    GdkEventAny *event)
-{
-  /* destroy the window after the main loop */
-  gtk_main_quit ();
-  return TRUE;
-}
-
-
-
 static void
 xfce_appfinder_window_update_image (XfceAppfinderWindow *window,
                                     GdkPixbuf           *pixbuf)
@@ -396,14 +430,30 @@ xfce_appfinder_window_set_padding (GtkWidget *entry,
 static void
 xfce_appfinder_window_entry_changed (XfceAppfinderWindow *window)
 {
-  const gchar *text;
-  GdkPixbuf   *pixbuf;
+  const gchar  *text;
+  GdkPixbuf    *pixbuf;
+  gchar        *normalized;
+  GtkTreeModel *model;
 
   text = gtk_entry_get_text (GTK_ENTRY (window->entry));
 
   if (gtk_widget_get_visible (window->paned))
     {
-      xfce_appfinder_model_filter_string (window->model, text);
+      g_free (window->filter_text);
+
+      if (IS_STRING (text))
+        {
+          normalized = g_utf8_normalize (text, -1, G_NORMALIZE_ALL);
+          window->filter_text = g_utf8_casefold (normalized, -1);
+          g_free (normalized);
+        }
+      else
+        {
+          window->filter_text = NULL;
+        }
+
+      model = gtk_tree_view_get_model (GTK_TREE_VIEW (window->treeview));
+      gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
     }
   else
     {
@@ -547,19 +597,37 @@ xfce_appfinder_window_category_changed (GtkTreeSelection    *selection,
     {
       gtk_tree_model_get (model, &iter, XFCE_APPFINDER_CATEGORY_MODEL_COLUMN_NAME, &category, -1);
 
-      if (g_utf8_collate (category, _("All Applications")) == 0)
-        xfce_appfinder_model_filter_category (window->model, NULL);
-      else if (g_utf8_collate (category, _("Commands History")) == 0)
-        xfce_appfinder_model_filter_category (window->model, "\0");
+      g_free (window->filter_category);
+
+      if (g_strcmp0 (category, _("All Applications")) == 0)
+        window->filter_category = NULL;
+      else if (g_strcmp0 (category, _("Commands History")) == 0)
+        window->filter_category = g_strdup ("\0");
       else
-        xfce_appfinder_model_filter_category (window->model, category);
+        window->filter_category = g_strdup (category);
 
       g_free (category);
+
+      model = gtk_tree_view_get_model (GTK_TREE_VIEW (window->treeview));
+      gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
     }
 }
 
 
 
+static gboolean
+xfce_appfinder_window_item_visible (GtkTreeModel *model,
+                                    GtkTreeIter  *iter,
+                                    gpointer      data)
+{
+  XfceAppfinderWindow *window = XFCE_APPFINDER_WINDOW (data);
+
+  return xfce_appfinder_model_get_visible (XFCE_APPFINDER_MODEL (model), iter,
+                                           window->filter_category, window->filter_text);
+}
+
+
+
 static void
 xfce_appfinder_window_item_changed (XfceAppfinderWindow *window)
 {
@@ -726,15 +794,7 @@ xfce_appfinder_window_execute (XfceAppfinderWindow *window)
     }
 
   if (result)
-    gtk_main_quit ();
-}
-
-
-
-GtkWidget *
-xfce_appfinder_window_new (void)
-{
-  return g_object_new (XFCE_TYPE_APPFINDER_WINDOW, NULL);
+    gtk_widget_destroy (GTK_WIDGET (window));
 }
 
 
diff --git a/src/appfinder-window.h b/src/appfinder-window.h
index f100f66..95b935d 100644
--- a/src/appfinder-window.h
+++ b/src/appfinder-window.h
@@ -35,8 +35,6 @@ typedef struct _XfceAppfinderWindow      XfceAppfinderWindow;
 
 GType      xfce_appfinder_window_get_type     (void) G_GNUC_CONST;
 
-GtkWidget *xfce_appfinder_window_new          (void) G_GNUC_MALLOC;
-
 void       xfce_appfinder_window_set_expanded (XfceAppfinderWindow *window,
                                                gboolean             expanded);
 
diff --git a/src/main.c b/src/main.c
index 5750b82..0e5cd8f 100644
--- a/src/main.c
+++ b/src/main.c
@@ -27,34 +27,255 @@
 #include <gtk/gtk.h>
 #include <libxfce4util/libxfce4util.h>
 #include <garcon/garcon.h>
+#include <xfconf/xfconf.h>
+
+#include <dbus/dbus.h>
+#include <dbus/dbus-glib-lowlevel.h>
 
-#include <src/appfinder-model.h>
 #include <src/appfinder-window.h>
+#include <src/appfinder-private.h>
+
+
 
+#define APPFINDER_DBUS_SERVICE     "org.xfce.Appfinder"
+#define APPFINDER_DBUS_INTERFACE   APPFINDER_DBUS_SERVICE
+#define APPFINDER_DBUS_PATH        "/org/xfce/Appfinder"
+#define APPFINDER_DBUS_METHOD_OPEN "OpenWindow"
+#define APPFINDER_DBUS_METHOD_QUIT "Quit"
+#define APPFINDER_DBUS_ERROR       APPFINDER_DBUS_SERVICE ".Error"
 
 
-static gboolean  opt_finder = FALSE;
+
+static gboolean  opt_expanded = FALSE;
 static gboolean  opt_version = FALSE;
+static gboolean  opt_replace = FALSE;
+static gboolean  opt_quit = FALSE;
 static gchar    *opt_filename = NULL;
+static GSList   *windows = NULL;
+static gboolean  service_owner = FALSE;
 
 
 
 static GOptionEntry option_entries[] =
 {
-  { "finder", '\0', 0, G_OPTION_ARG_NONE, &opt_finder, N_("Start in expanded mode"), NULL },
+  { "expanded", 'x', 0, G_OPTION_ARG_NONE, &opt_expanded, N_("Start in expanded mode"), NULL },
   { "version", 'V', 0, G_OPTION_ARG_NONE, &opt_version, N_("Print version information and exit"), NULL },
+  { "replace", 'r', 0, G_OPTION_ARG_NONE, &opt_replace, N_("Replace the existing service"), NULL },
+  { "quit", 'q', 0, G_OPTION_ARG_NONE, &opt_quit, N_("Quit all instances"), NULL },
   { G_OPTION_REMAINING, '\0', 0, G_OPTION_ARG_FILENAME, &opt_filename, NULL, NULL },
   { NULL }
 };
 
 
 
+static void appfinder_dbus_unregister (DBusConnection *dbus_connection);
+
+
+
+static void
+appfinder_window_destroyed (GtkWidget *window)
+{
+  if (windows == NULL)
+    return;
+
+  /* remove from internal list */
+  windows = g_slist_remove (windows, window);
+
+  /* leave if all windows are closed and we're not service owner */
+  if (windows == NULL && !service_owner)
+    gtk_main_quit ();
+}
+
+
+
+static void
+appfinder_window_new (const gchar *startup_id,
+                      gboolean     expanded,
+                      const gchar *menu_filename)
+{
+  GtkWidget *window;
+
+  window = g_object_new (XFCE_TYPE_APPFINDER_WINDOW,
+                         "startup-id", IS_STRING (startup_id) ? startup_id : NULL,
+                         NULL);
+  xfce_appfinder_window_set_expanded (XFCE_APPFINDER_WINDOW (window), expanded);
+  gtk_widget_show (window);
+
+  windows = g_slist_prepend (windows, window);
+  g_signal_connect (G_OBJECT (window), "destroy",
+                    G_CALLBACK (appfinder_window_destroyed), NULL);
+}
+
+
+
+static DBusHandlerResult
+appfinder_dbus_message (DBusConnection *dbus_connection,
+                        DBusMessage    *message,
+                        gpointer        user_data)
+{
+  DBusMessage *reply;
+  gboolean     expanded;
+  gchar       *startup_id;
+  DBusError    derror;
+  gchar       *menu_filename;
+
+  if (dbus_message_is_method_call (message, APPFINDER_DBUS_INTERFACE, APPFINDER_DBUS_METHOD_OPEN))
+    {
+      dbus_error_init (&derror);
+      if (dbus_message_get_args (message, &derror,
+                                 DBUS_TYPE_BOOLEAN, &expanded,
+                                 DBUS_TYPE_STRING, &startup_id,
+                                 DBUS_TYPE_STRING, &menu_filename,
+                                 DBUS_TYPE_INVALID))
+        {
+          appfinder_window_new (startup_id, expanded, menu_filename);
+          reply = dbus_message_new_method_return (message);
+        }
+      else
+        {
+          reply = dbus_message_new_error (message, APPFINDER_DBUS_ERROR, derror.message);
+          dbus_error_free (&derror);
+        }
+
+      dbus_connection_send (dbus_connection, reply, NULL);
+      dbus_message_unref (reply);
+    }
+  else if (dbus_message_is_method_call (message, APPFINDER_DBUS_INTERFACE, APPFINDER_DBUS_METHOD_QUIT))
+    {
+      /* close all windows and quit */
+      g_printerr ("%s: %s.\n", PACKAGE_NAME, _("Forced to quit"));
+      gtk_main_quit ();
+    }
+  else if (dbus_message_is_signal (message, DBUS_INTERFACE_LOCAL, "Disconnected")
+           || dbus_message_is_signal (message, DBUS_INTERFACE_DBUS, "NameOwnerChanged"))
+    {
+      if (windows != NULL)
+        {
+          /* don't respond to dbus signals and close on last window */
+          appfinder_dbus_unregister (dbus_connection);
+        }
+      else
+        {
+          /* no active windows, just exit the instance */
+          gtk_main_quit ();
+        }
+    }
+  else
+    {
+      return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+    }
+
+  return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+
+
+static gboolean
+appfinder_dbus_open_window (DBusConnection *dbus_connection,
+                            const gchar    *startup_id,
+                            const gchar    *menu_filename)
+{
+
+  DBusError    derror;
+  DBusMessage *method, *result;
+
+  method = dbus_message_new_method_call (APPFINDER_DBUS_SERVICE,
+                                         APPFINDER_DBUS_PATH,
+                                         APPFINDER_DBUS_INTERFACE,
+                                         APPFINDER_DBUS_METHOD_OPEN);
+
+  if (startup_id == NULL)
+    startup_id = "";
+  if (menu_filename == NULL)
+    menu_filename = "";
+
+  dbus_message_append_args (method,
+                            DBUS_TYPE_BOOLEAN, &opt_expanded,
+                            DBUS_TYPE_STRING, &startup_id,
+                            DBUS_TYPE_STRING, &menu_filename,
+                            DBUS_TYPE_INVALID);
+
+  dbus_message_set_auto_start (method, TRUE);
+  dbus_error_init (&derror);
+  result = dbus_connection_send_with_reply_and_block (dbus_connection, method, 5000, &derror);
+  dbus_message_unref (method);
+
+  if (G_UNLIKELY (result == NULL))
+    {
+       g_critical ("Failed to open window: %s", derror.message);
+       dbus_error_free(&derror);
+       return FALSE;
+    }
+
+  dbus_message_unref (result);
+
+  return TRUE;
+}
+
+
+
+static gint
+appfinder_dbus_quit (void)
+{
+  DBusMessage    *method;
+  DBusConnection *dbus_connection;
+  DBusError       derror;
+  gboolean        succeed = FALSE;
+
+  dbus_error_init (&derror);
+  dbus_connection = dbus_bus_get (DBUS_BUS_SESSION, &derror);
+  if (G_LIKELY (dbus_connection != NULL))
+    {
+      method = dbus_message_new_method_call (APPFINDER_DBUS_SERVICE,
+                                             APPFINDER_DBUS_PATH,
+                                             APPFINDER_DBUS_INTERFACE,
+                                             APPFINDER_DBUS_METHOD_QUIT);
+
+      dbus_message_set_auto_start (method, FALSE);
+      succeed = dbus_connection_send (dbus_connection, method, NULL);
+      dbus_message_unref (method);
+
+      dbus_connection_flush (dbus_connection);
+      dbus_connection_unref (dbus_connection);
+    }
+  else
+    {
+      g_warning ("Unable to open D-Bus connection: %s", derror.message);
+      dbus_error_free (&derror);
+    }
+
+  return succeed ? EXIT_SUCCESS : EXIT_FAILURE;
+}
+
+
+
+static void
+appfinder_dbus_unregister (DBusConnection *dbus_connection)
+{
+  if (service_owner)
+    {
+      service_owner = FALSE;
+
+      dbus_connection_remove_filter (dbus_connection, appfinder_dbus_message, NULL);
+      dbus_connection_unregister_object_path (dbus_connection, APPFINDER_DBUS_PATH);
+      dbus_bus_release_name (dbus_connection, APPFINDER_DBUS_SERVICE, NULL);
+    }
+}
+
+
+
 gint
 main (gint argc, gchar **argv)
 {
-  GError      *error = NULL;
-  GtkWidget   *window;
-  const gchar *desktop;
+  GError               *error = NULL;
+  const gchar          *desktop;
+  DBusConnection       *dbus_connection;
+  guint                 dbus_flags;
+  DBusObjectPathVTable  vtable = { NULL, appfinder_dbus_message, NULL, };
+  gint                  result;
+  const gchar          *startup_id;
+  DBusError             derror;
+  GSList               *windows_destroy;
 
   /* set translation domain */
   xfce_textdomain (GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR, "UTF-8");
@@ -68,6 +289,9 @@ main (gint argc, gchar **argv)
   if (!g_thread_supported ())
     g_thread_init (NULL);
 
+  /* get the startup notification id */
+  startup_id = g_getenv ("DESKTOP_STARTUP_ID");
+
   if (!gtk_init_with_args (&argc, &argv, _("[MENUFILE]"), option_entries, GETTEXT_PACKAGE, &error))
     {
       g_printerr ("%s: %s.\n", PACKAGE_NAME, error->message);
@@ -89,6 +313,9 @@ main (gint argc, gchar **argv)
       return EXIT_SUCCESS;
     }
 
+  if (opt_quit)
+    return appfinder_dbus_quit ();
+
   /* if the value is unset, fallback to XFCE, if the
    * value is empty, allow all applications in the menu */
   desktop = g_getenv ("XDG_CURRENT_DESKTOP");
@@ -98,16 +325,86 @@ main (gint argc, gchar **argv)
     desktop = NULL;
   garcon_set_environment (desktop);
 
-  window = xfce_appfinder_window_new ();
-  xfce_appfinder_window_set_expanded (XFCE_APPFINDER_WINDOW (window), opt_finder);
-  gtk_widget_show (window);
+  xfconf_init (NULL);
+
+  /* become the serivce owner or ask the current owner to spawn an instance */
+  dbus_error_init (&derror);
+  dbus_connection = dbus_bus_get (DBUS_BUS_SESSION, &derror);
+  if (G_LIKELY (dbus_connection != NULL))
+    {
+      dbus_connection_set_exit_on_disconnect (dbus_connection, FALSE);
+
+      dbus_flags = DBUS_NAME_FLAG_DO_NOT_QUEUE | DBUS_NAME_FLAG_ALLOW_REPLACEMENT;
+      if (opt_replace)
+        dbus_flags |= DBUS_NAME_FLAG_REPLACE_EXISTING;
+
+      result = dbus_bus_request_name (dbus_connection, APPFINDER_DBUS_SERVICE, dbus_flags, &derror);
+      if (result == DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER)
+        {
+          dbus_connection_setup_with_g_main (dbus_connection, NULL);
+
+          /* watch owner changes */
+          dbus_bus_add_match (dbus_connection, "type='signal',member='NameOwnerChanged',"
+                                               "arg0='"APPFINDER_DBUS_SERVICE"'", NULL);
+
+          /* method handling for the appfinder */
+          if (dbus_connection_register_object_path (dbus_connection, APPFINDER_DBUS_PATH, &vtable, NULL)
+              && dbus_connection_add_filter (dbus_connection, appfinder_dbus_message, NULL, NULL))
+            {
+              APPFINDER_DEBUG ("registered dbus service");
+
+              /* successfully registered the service */
+              service_owner = TRUE;
+            }
+          else
+            {
+              g_warning ("Failed to register D-Bus filter or vtable");
+            }
+        }
+      else if (result == DBUS_REQUEST_NAME_REPLY_EXISTS)
+        {
+          if (appfinder_dbus_open_window (dbus_connection, startup_id, opt_filename))
+            {
+               /* successfully opened a window in the other instance */
+               dbus_connection_unref (dbus_connection);
+               return EXIT_SUCCESS;
+            }
+        }
+      else
+        {
+          g_warning ("Unable to request D-Bus name: %s", derror.message);
+          dbus_error_free (&derror);
+        }
+    }
+  else
+    {
+      g_warning ("Unable to open D-Bus connection: %s", derror.message);
+      dbus_error_free (&derror);
+    }
+
+  /* create initial window */
+  appfinder_window_new (NULL, opt_expanded, opt_filename);
 
   APPFINDER_DEBUG ("enter mainloop");
 
   gtk_main ();
 
-  gtk_widget_hide (window);
-  gtk_widget_destroy (window);
+  xfconf_shutdown ();
+
+  if (G_LIKELY (dbus_connection != NULL))
+    {
+      appfinder_dbus_unregister (dbus_connection);
+      dbus_connection_unref (dbus_connection);
+    }
+
+  if (windows != NULL)
+    {
+      windows_destroy = windows;
+      windows = NULL;
+
+      /* destroy all windows without poking gtk_main_quit */
+      g_slist_foreach (windows_destroy, (GFunc) gtk_widget_destroy, NULL);
+    }
 
   return EXIT_SUCCESS;
 }



More information about the Xfce4-commits mailing list