[Xfce4-commits] <xfce-utils:completion> Add desktop file based completion.
Jérôme Guelfucci
noreply at xfce.org
Fri Nov 5 18:26:01 CET 2010
Updating branch refs/heads/completion
to 912df5b2c31068bfe6679cd6471aed148e69707c (commit)
from b19bc6f0113e1b76ca21b953b9bed9469493e085 (commit)
commit 912df5b2c31068bfe6679cd6471aed148e69707c
Author: Jérôme Guelfucci <jeromeg at xfce.org>
Date: Fri Nov 5 18:09:28 2010 +0100
Add desktop file based completion.
This uses some slightly modified code from exo.
configure.in.in | 1 +
xfrun/Makefile.am | 6 +-
xfrun/exo-die-desktop-model.c | 750 +++++++++++++++++++++++++++++++++++++++++
xfrun/exo-die-desktop-model.h | 72 ++++
xfrun/xfrun-dialog.c | 58 ++++
5 files changed, 886 insertions(+), 1 deletions(-)
diff --git a/configure.in.in b/configure.in.in
index 67b3baf..5205cae 100644
--- a/configure.in.in
+++ b/configure.in.in
@@ -110,6 +110,7 @@ dnl Check for required packages
XDT_CHECK_PACKAGE([GTK], [gtk+-2.0], [2.10.0])
XDT_CHECK_PACKAGE([LIBXFCE4UTIL], [libxfce4util-1.0], [4.6.0])
XDT_CHECK_PACKAGE([LIBXFCE4UI], [libxfce4ui-1], [4.7.0])
+XDT_CHECK_PACKAGE([EXO], [exo-1], [0.5.1])
dnl check for dbus and libdbus-glib
AM_CONDITIONAL([HAVE_DBUS], [test "x$DBUS_FOUND" = "xyes"])
diff --git a/xfrun/Makefile.am b/xfrun/Makefile.am
index 0a76417..b024526 100644
--- a/xfrun/Makefile.am
+++ b/xfrun/Makefile.am
@@ -4,7 +4,9 @@ xfrun4_SOURCES = \
xfrun-history.c \
xfrun-history.h \
xfrun-dialog.c \
- xfrun-dialog.h
+ xfrun-dialog.h \
+ exo-die-desktop-model.c \
+ exo-die-desktop-model.h
if HAVE_DBUS
@@ -23,12 +25,14 @@ xfrun4_CFLAGS = \
-DDBUS_API_SUBJECT_TO_CHANGE \
@LIBXFCE4UTIL_CFLAGS@ \
@LIBXFCE4UI_CFLAGS@ \
+ @EXO_CFLAGS@ \
@GTK_CFLAGS@ \
@DBUS_CFLAGS@
xfrun4_LDADD = \
@LIBXFCE4UTIL_LIBS@ \
@LIBXFCE4UI_LIBS@ \
+ @EXO_LIBS@ \
@GTK_LIBS@ \
@DBUS_LIBS@
diff --git a/xfrun/exo-die-desktop-model.c b/xfrun/exo-die-desktop-model.c
new file mode 100644
index 0000000..5d53d51
--- /dev/null
+++ b/xfrun/exo-die-desktop-model.c
@@ -0,0 +1,750 @@
+/*-
+ * Copyright (c) 2006 Benedikt Meurer <benny at xfce.org>.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef HAVE_MEMORY_H
+#include <memory.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include "exo-die-desktop-model.h"
+
+
+
+typedef struct _ExoDieDesktopItem ExoDieDesktopItem;
+
+
+
+static void exo_die_desktop_model_tree_model_init (GtkTreeModelIface *iface);
+static void exo_die_desktop_model_finalize (GObject *object);
+static GtkTreeModelFlags exo_die_desktop_model_get_flags (GtkTreeModel *tree_model);
+static gint exo_die_desktop_model_get_n_columns (GtkTreeModel *tree_model);
+static GType exo_die_desktop_model_get_column_type (GtkTreeModel *tree_model,
+ gint column);
+static gboolean exo_die_desktop_model_get_iter (GtkTreeModel *tree_model,
+ GtkTreeIter *iter,
+ GtkTreePath *path);
+static GtkTreePath *exo_die_desktop_model_get_path (GtkTreeModel *tree_model,
+ GtkTreeIter *iter);
+static void exo_die_desktop_model_get_value (GtkTreeModel *tree_model,
+ GtkTreeIter *iter,
+ gint column,
+ GValue *value);
+static gboolean exo_die_desktop_model_iter_next (GtkTreeModel *tree_model,
+ GtkTreeIter *iter);
+static gboolean exo_die_desktop_model_iter_children (GtkTreeModel *tree_model,
+ GtkTreeIter *iter,
+ GtkTreeIter *parent);
+static gboolean exo_die_desktop_model_iter_has_child (GtkTreeModel *tree_model,
+ GtkTreeIter *iter);
+static gint exo_die_desktop_model_iter_n_children (GtkTreeModel *tree_model,
+ GtkTreeIter *iter);
+static gboolean exo_die_desktop_model_iter_nth_child (GtkTreeModel *tree_model,
+ GtkTreeIter *iter,
+ GtkTreeIter *parent,
+ gint n);
+static gboolean exo_die_desktop_model_iter_parent (GtkTreeModel *tree_model,
+ GtkTreeIter *iter,
+ GtkTreeIter *child);
+static gboolean exo_die_desktop_model_collect_idle (gpointer user_data);
+static void exo_die_desktop_model_collect_idle_destroy (gpointer user_data);
+static GSList *exo_die_desktop_model_collect_readdir (ExoDieDesktopModel *desktop_model,
+ const gchar *dir_path);
+static gpointer exo_die_desktop_model_collect_thread (gpointer user_data);
+static ExoDieDesktopItem *exo_die_desktop_item_new_from_file (const gchar *file);
+static gint exo_die_desktop_item_compare (gconstpointer desktop_item_a,
+ gconstpointer desktop_item_b);
+static void exo_die_desktop_item_free (ExoDieDesktopItem *desktop_item);
+
+
+
+struct _ExoDieDesktopModelClass
+{
+ GObjectClass __parent__;
+};
+
+struct _ExoDieDesktopModel
+{
+ GObject __parent__;
+ gint stamp;
+ GSList *items;
+
+ gint collect_idle_id;
+ GSList *collect_items;
+ GThread *collect_thread;
+ volatile gboolean collect_cancelled;
+};
+
+struct _ExoDieDesktopItem
+{
+ gchar *command;
+ gchar *comment;
+ gchar *icon;
+ gchar *name;
+ guint snotify : 1;
+ guint terminal : 1;
+};
+
+
+
+G_DEFINE_TYPE_WITH_CODE (ExoDieDesktopModel, exo_die_desktop_model, G_TYPE_OBJECT,
+ G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_MODEL, exo_die_desktop_model_tree_model_init))
+
+
+
+static void
+exo_die_desktop_model_class_init (ExoDieDesktopModelClass *klass)
+{
+ GObjectClass *gobject_class;
+
+ gobject_class = G_OBJECT_CLASS (klass);
+ gobject_class->finalize = exo_die_desktop_model_finalize;
+}
+
+
+
+static void
+exo_die_desktop_model_tree_model_init (GtkTreeModelIface *iface)
+{
+ iface->get_flags = exo_die_desktop_model_get_flags;
+ iface->get_n_columns = exo_die_desktop_model_get_n_columns;
+ iface->get_column_type = exo_die_desktop_model_get_column_type;
+ iface->get_iter = exo_die_desktop_model_get_iter;
+ iface->get_path = exo_die_desktop_model_get_path;
+ iface->get_value = exo_die_desktop_model_get_value;
+ iface->iter_next = exo_die_desktop_model_iter_next;
+ iface->iter_children = exo_die_desktop_model_iter_children;
+ iface->iter_has_child = exo_die_desktop_model_iter_has_child;
+ iface->iter_n_children = exo_die_desktop_model_iter_n_children;
+ iface->iter_nth_child = exo_die_desktop_model_iter_nth_child;
+ iface->iter_parent = exo_die_desktop_model_iter_parent;
+}
+
+
+
+static void
+exo_die_desktop_model_init (ExoDieDesktopModel *desktop_model)
+{
+ /* generate a unique stamp */
+ desktop_model->stamp = g_random_int ();
+
+ /* spawn the collector thread */
+ desktop_model->collect_thread = g_thread_create_full (exo_die_desktop_model_collect_thread, desktop_model,
+ 0, TRUE, FALSE, G_THREAD_PRIORITY_LOW, NULL);
+}
+
+
+
+static void
+exo_die_desktop_model_finalize (GObject *object)
+{
+ ExoDieDesktopModel *desktop_model = EXO_DIE_DESKTOP_MODEL (object);
+
+ /* join the collector thread */
+ desktop_model->collect_cancelled = TRUE;
+ g_thread_join (desktop_model->collect_thread);
+
+ /* cancel any pending collect idle source */
+ if (G_UNLIKELY (desktop_model->collect_idle_id > 0))
+ g_source_remove (desktop_model->collect_idle_id);
+
+ /* release collected items (if any) */
+ g_slist_foreach (desktop_model->collect_items, (GFunc) exo_die_desktop_item_free, NULL);
+ g_slist_free (desktop_model->collect_items);
+
+ /* release all items */
+ g_slist_foreach (desktop_model->items, (GFunc) exo_die_desktop_item_free, NULL);
+ g_slist_free (desktop_model->items);
+
+ (*G_OBJECT_CLASS (exo_die_desktop_model_parent_class)->finalize) (object);
+}
+
+
+
+static GtkTreeModelFlags
+exo_die_desktop_model_get_flags (GtkTreeModel *tree_model)
+{
+ return GTK_TREE_MODEL_ITERS_PERSIST | GTK_TREE_MODEL_LIST_ONLY;
+}
+
+
+
+static gint
+exo_die_desktop_model_get_n_columns (GtkTreeModel *tree_model)
+{
+ return EXO_DIE_DESKTOP_MODEL_N_COLUMNS;
+}
+
+
+
+static GType
+exo_die_desktop_model_get_column_type (GtkTreeModel *tree_model,
+ gint column)
+{
+ switch (column)
+ {
+ case EXO_DIE_DESKTOP_MODEL_COLUMN_ABSTRACT:
+ case EXO_DIE_DESKTOP_MODEL_COLUMN_COMMAND:
+ case EXO_DIE_DESKTOP_MODEL_COLUMN_COMMENT:
+ case EXO_DIE_DESKTOP_MODEL_COLUMN_ICON:
+ case EXO_DIE_DESKTOP_MODEL_COLUMN_NAME:
+ return G_TYPE_STRING;
+
+ case EXO_DIE_DESKTOP_MODEL_COLUMN_SNOTIFY:
+ case EXO_DIE_DESKTOP_MODEL_COLUMN_TERMINAL:
+ return G_TYPE_BOOLEAN;
+
+ default:
+ g_assert_not_reached ();
+ return G_TYPE_INVALID;
+ }
+}
+
+
+
+static gboolean
+exo_die_desktop_model_get_iter (GtkTreeModel *tree_model,
+ GtkTreeIter *iter,
+ GtkTreePath *path)
+{
+ ExoDieDesktopModel *desktop_model = EXO_DIE_DESKTOP_MODEL (tree_model);
+
+ g_return_val_if_fail (EXO_DIE_IS_DESKTOP_MODEL (desktop_model), FALSE);
+ g_return_val_if_fail (gtk_tree_path_get_depth (path) > 0, FALSE);
+
+ iter->stamp = desktop_model->stamp;
+ iter->user_data = g_slist_nth (desktop_model->items, gtk_tree_path_get_indices (path)[0]);
+
+ return (iter->user_data != NULL);
+}
+
+
+
+static GtkTreePath*
+exo_die_desktop_model_get_path (GtkTreeModel *tree_model,
+ GtkTreeIter *iter)
+{
+ ExoDieDesktopModel *desktop_model = EXO_DIE_DESKTOP_MODEL (tree_model);
+ gint idx;
+
+ g_return_val_if_fail (EXO_DIE_IS_DESKTOP_MODEL (desktop_model), NULL);
+ g_return_val_if_fail (iter->stamp == desktop_model->stamp, NULL);
+
+ /* determine the index of the iter */
+ idx = g_slist_position (desktop_model->items, iter->user_data);
+ if (G_UNLIKELY (idx < 0))
+ return NULL;
+
+ return gtk_tree_path_new_from_indices (idx, -1);
+}
+
+
+
+static void
+exo_die_desktop_model_get_value (GtkTreeModel *tree_model,
+ GtkTreeIter *iter,
+ gint column,
+ GValue *value)
+{
+ ExoDieDesktopModel *desktop_model = EXO_DIE_DESKTOP_MODEL (tree_model);
+ ExoDieDesktopItem *desktop_item;
+ gchar *escaped;
+
+ g_return_if_fail (EXO_DIE_IS_DESKTOP_MODEL (desktop_model));
+ g_return_if_fail (iter->stamp == desktop_model->stamp);
+
+ desktop_item = g_slist_nth_data (iter->user_data, 0);
+
+ switch (column)
+ {
+ case EXO_DIE_DESKTOP_MODEL_COLUMN_ABSTRACT:
+ g_value_init (value, G_TYPE_STRING);
+ escaped = g_markup_escape_text (desktop_item->name, -1);
+ g_value_take_string (value, g_strdup_printf (_("Create Launcher <b>%s</b>"), escaped));
+ g_free (escaped);
+ break;
+
+ case EXO_DIE_DESKTOP_MODEL_COLUMN_COMMAND:
+ g_value_init (value, G_TYPE_STRING);
+ g_value_set_static_string (value, desktop_item->command);
+ break;
+
+ case EXO_DIE_DESKTOP_MODEL_COLUMN_COMMENT:
+ g_value_init (value, G_TYPE_STRING);
+ g_value_set_static_string (value, desktop_item->comment);
+ break;
+
+ case EXO_DIE_DESKTOP_MODEL_COLUMN_ICON:
+ g_value_init (value, G_TYPE_STRING);
+ g_value_set_static_string (value, desktop_item->icon);
+ break;
+
+ case EXO_DIE_DESKTOP_MODEL_COLUMN_NAME:
+ g_value_init (value, G_TYPE_STRING);
+ g_value_set_static_string (value, desktop_item->name);
+ break;
+
+ case EXO_DIE_DESKTOP_MODEL_COLUMN_SNOTIFY:
+ g_value_init (value, G_TYPE_BOOLEAN);
+ g_value_set_boolean (value, desktop_item->snotify);
+ break;
+
+ case EXO_DIE_DESKTOP_MODEL_COLUMN_TERMINAL:
+ g_value_init (value, G_TYPE_BOOLEAN);
+ g_value_set_boolean (value, desktop_item->terminal);
+ break;
+
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+}
+
+
+
+static gboolean
+exo_die_desktop_model_iter_next (GtkTreeModel *tree_model,
+ GtkTreeIter *iter)
+{
+ g_return_val_if_fail (EXO_DIE_IS_DESKTOP_MODEL (tree_model), FALSE);
+ g_return_val_if_fail (iter->stamp == EXO_DIE_DESKTOP_MODEL (tree_model)->stamp, FALSE);
+
+ iter->user_data = g_slist_next (iter->user_data);
+ return (iter->user_data != NULL);
+}
+
+
+
+static gboolean
+exo_die_desktop_model_iter_children (GtkTreeModel *tree_model,
+ GtkTreeIter *iter,
+ GtkTreeIter *parent)
+{
+ ExoDieDesktopModel *desktop_model = EXO_DIE_DESKTOP_MODEL (tree_model);
+
+ g_return_val_if_fail (EXO_DIE_IS_DESKTOP_MODEL (desktop_model), FALSE);
+
+ if (G_LIKELY (parent == NULL && desktop_model->items != NULL))
+ {
+ iter->stamp = desktop_model->stamp;
+ iter->user_data = desktop_model->items;
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+
+static gboolean
+exo_die_desktop_model_iter_has_child (GtkTreeModel *tree_model,
+ GtkTreeIter *iter)
+{
+ return FALSE;
+}
+
+
+
+static gint
+exo_die_desktop_model_iter_n_children (GtkTreeModel *tree_model,
+ GtkTreeIter *iter)
+{
+ ExoDieDesktopModel *desktop_model = EXO_DIE_DESKTOP_MODEL (tree_model);
+
+ g_return_val_if_fail (EXO_DIE_IS_DESKTOP_MODEL (desktop_model), 0);
+
+ return (iter == NULL) ? g_slist_length (desktop_model->items) : 0;
+}
+
+
+
+static gboolean
+exo_die_desktop_model_iter_nth_child (GtkTreeModel *tree_model,
+ GtkTreeIter *iter,
+ GtkTreeIter *parent,
+ gint n)
+{
+ ExoDieDesktopModel *desktop_model = EXO_DIE_DESKTOP_MODEL (tree_model);
+
+ g_return_val_if_fail (EXO_DIE_IS_DESKTOP_MODEL (desktop_model), FALSE);
+
+ if (G_LIKELY (parent != NULL))
+ {
+ iter->stamp = desktop_model->stamp;
+ iter->user_data = g_slist_nth (desktop_model->items, n);
+ return (iter->user_data != NULL);
+ }
+
+ return FALSE;
+}
+
+
+
+static gboolean
+exo_die_desktop_model_iter_parent (GtkTreeModel *tree_model,
+ GtkTreeIter *iter,
+ GtkTreeIter *child)
+{
+ return FALSE;
+}
+
+
+
+static gboolean
+exo_die_desktop_model_collect_idle (gpointer user_data)
+{
+ ExoDieDesktopModel *desktop_model = EXO_DIE_DESKTOP_MODEL (user_data);
+ GtkTreePath *path;
+ GtkTreeIter iter;
+ GSList *lp;
+ GSList *np;
+
+ g_return_val_if_fail (EXO_DIE_IS_DESKTOP_MODEL (desktop_model), FALSE);
+ g_return_val_if_fail (desktop_model->items == NULL, FALSE);
+
+ GDK_THREADS_ENTER ();
+
+ /* move the collected items "online" */
+ desktop_model->items = desktop_model->collect_items;
+ desktop_model->collect_items = NULL;
+
+ /* emit notifications for all new items */
+ path = gtk_tree_path_new_first ();
+ for (lp = desktop_model->items; lp != NULL; lp = lp->next)
+ {
+ /* remember the next item */
+ np = lp->next;
+ lp->next = NULL;
+
+ /* generate the iterator */
+ iter.stamp = desktop_model->stamp;
+ iter.user_data = lp;
+
+ /* emit the "row-inserted" signal */
+ gtk_tree_model_row_inserted (GTK_TREE_MODEL (desktop_model), path, &iter);
+
+ /* advance the path */
+ gtk_tree_path_next (path);
+
+ /* reset the next item */
+ lp->next = np;
+ }
+ gtk_tree_path_free (path);
+
+ GDK_THREADS_LEAVE ();
+
+ return FALSE;
+}
+
+
+
+static void
+exo_die_desktop_model_collect_idle_destroy (gpointer user_data)
+{
+ EXO_DIE_DESKTOP_MODEL (user_data)->collect_idle_id = 0;
+}
+
+
+
+static GSList*
+exo_die_desktop_model_collect_readdir (ExoDieDesktopModel *desktop_model,
+ const gchar *dir_path)
+{
+ ExoDieDesktopItem *desktop_item;
+ const gchar *name;
+ GSList *items = NULL;
+ gchar *path;
+ GDir *dp;
+
+ /* try to open the directory */
+ dp = g_dir_open (dir_path, 0, NULL);
+ if (G_LIKELY (dp != NULL))
+ {
+ /* process the files within this directory */
+ while (!desktop_model->collect_cancelled)
+ {
+ /* read the next file entry */
+ name = g_dir_read_name (dp);
+ if (G_UNLIKELY (name == NULL))
+ break;
+
+ /* generate the absolute path to the file entry */
+ path = g_build_filename (dir_path, name, NULL);
+
+ /* check if we have a directory or a regular file here */
+ if (g_file_test (path, G_FILE_TEST_IS_DIR))
+ {
+ /* recurse for directories */
+ items = g_slist_concat (items, exo_die_desktop_model_collect_readdir (desktop_model, path));
+ }
+ else if (g_file_test (path, G_FILE_TEST_IS_REGULAR) && g_str_has_suffix (name, ".desktop"))
+ {
+ /* try to parse the .desktop file */
+ desktop_item = exo_die_desktop_item_new_from_file (path);
+ if (G_LIKELY (desktop_item != NULL))
+ items = g_slist_prepend (items, desktop_item);
+ }
+
+ /* cleanup */
+ g_free (path);
+ }
+
+ /* close the directory handle */
+ g_dir_close (dp);
+ }
+
+ return items;
+}
+
+
+
+static gpointer
+exo_die_desktop_model_collect_thread (gpointer user_data)
+{
+ ExoDieDesktopModel *desktop_model = EXO_DIE_DESKTOP_MODEL (user_data);
+ GSList *items = NULL;
+ gchar **paths;
+ guint n;
+
+ /* determine the available applications/ directories */
+ paths = xfce_resource_lookup_all (XFCE_RESOURCE_DATA, "applications/");
+ for (n = 0; !desktop_model->collect_cancelled && paths[n] != NULL; ++n)
+ {
+ /* collect this directory */
+ items = g_slist_concat (items, exo_die_desktop_model_collect_readdir (desktop_model, paths[n]));
+ }
+
+ /* check if we're still active */
+ if (G_LIKELY (!desktop_model->collect_cancelled && items != NULL))
+ {
+ /* tell the model about the items (sorting the items by their names) */
+ desktop_model->collect_items = g_slist_sort (items, exo_die_desktop_item_compare);
+
+ /* and schedule an idle source */
+ desktop_model->collect_idle_id = g_idle_add_full (G_PRIORITY_LOW, exo_die_desktop_model_collect_idle,
+ desktop_model, exo_die_desktop_model_collect_idle_destroy);
+ }
+ else
+ {
+ /* release the collected items */
+ g_slist_foreach (items, (GFunc) exo_die_desktop_item_free, NULL);
+ g_slist_free (items);
+ }
+
+ /* cleanup */
+ g_strfreev (paths);
+
+ return NULL;
+}
+
+
+
+static ExoDieDesktopItem*
+exo_die_desktop_item_new_from_file (const gchar *file)
+{
+ ExoDieDesktopItem *desktop_item = NULL;
+ const gchar *comment;
+ const gchar *icon;
+ const gchar *name;
+ const gchar *type;
+ gchar *command;
+ XfceRc *rc;
+ gint icon_len;
+
+ /* try to open the file */
+ rc = xfce_rc_simple_open (file, TRUE);
+ if (G_LIKELY (rc != NULL))
+ {
+ /* skip hidden items to avoid confusion */
+ xfce_rc_set_group (rc, "Desktop Entry");
+ if (!xfce_rc_read_bool_entry (rc, "Hidden", FALSE) && !xfce_rc_read_bool_entry (rc, "NoDisplay", FALSE))
+ {
+ GString *newstr;
+ gchar *p;
+
+ /* determine the attributes from the file */
+ command = g_strdup (xfce_rc_read_entry_untranslated (rc, "Exec", NULL));
+ comment = xfce_rc_read_entry (rc, "Comment", NULL);
+ icon = xfce_rc_read_entry (rc, "Icon", NULL);
+ name = xfce_rc_read_entry (rc, "Name", NULL);
+ type = xfce_rc_read_entry (rc, "Type", "Application");
+
+ /* Remove %u, %U, %f and %F from command */
+ newstr = g_string_sized_new (strlen (command) + 1 );
+
+ for (p = command; *p; ++p)
+ {
+ if (*p == '%')
+ {
+ ++p;
+
+ if (!(*p == 'u' || *p == 'U' || *p =='F' || *p == 'f'))
+ g_string_append_c (newstr, *p);
+ }
+ else
+ g_string_append_c (newstr, *p);
+ }
+
+ g_free (command);
+ command = newstr->str;
+
+ /* check if the required attributes were found (and we have an Application) */
+ if (G_LIKELY (strcmp (type, "Application") == 0 && command != NULL && name != NULL))
+ {
+ /* allocate the desktop item */
+ desktop_item = g_new (ExoDieDesktopItem, 1);
+ desktop_item->command = g_strdup (command);
+ desktop_item->comment = g_strdup (comment);
+ desktop_item->icon = g_strdup (icon);
+ desktop_item->name = g_strdup (name);
+
+ /* strip off known extensions from the icon */
+ if (G_LIKELY (desktop_item->icon != NULL))
+ {
+ /* check if ends with ".png" */
+ icon_len = strlen (desktop_item->icon);
+ if (icon_len > 4 && strcmp (desktop_item->icon + (icon_len - 4), ".png") == 0)
+ desktop_item->icon[icon_len - 4] = '\0';
+ }
+
+ /* strip the "Xfce 4 " prefix from the names */
+ if (strncmp (desktop_item->name, "Xfce 4 ", 7) == 0)
+ {
+ /* release the full name */
+ g_free (desktop_item->name);
+
+ /* use the short name */
+ desktop_item->name = g_strdup (name + 7);
+ }
+
+ /* check if startup notification is supported */
+ desktop_item->snotify = (xfce_rc_read_bool_entry (rc, "StartupNotify", FALSE) || xfce_rc_read_bool_entry (rc, "X-KDE-StartupNotify", FALSE));
+
+ /* check if should be run in terminal */
+ desktop_item->terminal = xfce_rc_read_bool_entry (rc, "Terminal", FALSE) ? TRUE : FALSE;
+ }
+ }
+
+ /* close the file */
+ xfce_rc_close (rc);
+ }
+
+ return desktop_item;
+}
+
+
+
+static gint
+exo_die_desktop_item_compare (gconstpointer desktop_item_a,
+ gconstpointer desktop_item_b)
+{
+ return g_utf8_collate (((ExoDieDesktopItem *) desktop_item_a)->name, ((ExoDieDesktopItem *) desktop_item_b)->name);
+}
+
+
+
+static void
+exo_die_desktop_item_free (ExoDieDesktopItem *desktop_item)
+{
+ g_free (desktop_item->command);
+ g_free (desktop_item->comment);
+ g_free (desktop_item->icon);
+ g_free (desktop_item->name);
+ g_free (desktop_item);
+}
+
+
+
+/**
+ * exo_die_desktop_model_new:
+ *
+ * Allocates a new #ExoDieDesktopModel instance.
+ *
+ * Return value: the newly allocated #ExoDieDesktopModel.
+ **/
+ExoDieDesktopModel*
+exo_die_desktop_model_new (void)
+{
+ return g_object_new (EXO_DIE_TYPE_DESKTOP_MODEL, NULL);
+}
+
+
+
+/**
+ * exo_die_desktop_model_match_func:
+ * @completion : a #GtkEntryCompletion.
+ * @key : the text to match.
+ * @iter : a valid #GtkTreeIter for the row to match.
+ * @user_data : a #ExoDieDesktopModel.
+ *
+ * Convenience function to match the @iter with the specified @key.
+ *
+ * Return value: %TRUE if @iter is a possible match, %FALSE otherwise.
+ **/
+gboolean
+exo_die_desktop_model_match_func (GtkEntryCompletion *completion,
+ const gchar *key,
+ GtkTreeIter *iter,
+ gpointer user_data)
+{
+ ExoDieDesktopModel *desktop_model = EXO_DIE_DESKTOP_MODEL (user_data);
+ ExoDieDesktopItem *desktop_item;
+ gboolean matches = FALSE;
+ gchar *casefolded;
+ gchar *normalized;
+
+ g_return_val_if_fail (EXO_DIE_IS_DESKTOP_MODEL (desktop_model), FALSE);
+ g_return_val_if_fail (GTK_IS_ENTRY_COMPLETION (completion), FALSE);
+ g_return_val_if_fail (iter->stamp == desktop_model->stamp, FALSE);
+ g_return_val_if_fail (g_utf8_validate (key, -1, NULL), FALSE);
+
+ /* determine the item for the iter */
+ desktop_item = g_slist_nth_data (iter->user_data, 0);
+
+ /* check if the name matches */
+ if (G_LIKELY (desktop_item->name != NULL))
+ {
+ normalized = g_utf8_normalize (desktop_item->name, -1, G_NORMALIZE_ALL);
+ casefolded = g_utf8_casefold (normalized, -1);
+ if (G_LIKELY (casefolded != NULL && key != NULL))
+ matches = (strstr (casefolded, key) != NULL);
+ g_free (casefolded);
+ g_free (normalized);
+ }
+
+ /* check if no hit yet */
+ if (G_LIKELY (!matches && desktop_item->comment != NULL))
+ {
+ /* also check the comment then */
+ normalized = g_utf8_normalize (desktop_item->comment, -1, G_NORMALIZE_ALL);
+ casefolded = g_utf8_casefold (normalized, -1);
+ if (G_LIKELY (casefolded != NULL && key != NULL))
+ matches = (strstr (casefolded, key) != NULL);
+ g_free (casefolded);
+ g_free (normalized);
+ }
+
+ return matches;
+}
+
diff --git a/xfrun/exo-die-desktop-model.h b/xfrun/exo-die-desktop-model.h
new file mode 100644
index 0000000..e2e1d47
--- /dev/null
+++ b/xfrun/exo-die-desktop-model.h
@@ -0,0 +1,72 @@
+/*-
+ * Copyright (c) 2006 Benedikt Meurer <benny at xfce.org>.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __EXO_DIE_DESKTOP_MODEL_H__
+#define __EXO_DIE_DESKTOP_MODEL_H__
+
+#include <exo/exo.h>
+
+G_BEGIN_DECLS;
+
+typedef struct _ExoDieDesktopModelClass ExoDieDesktopModelClass;
+typedef struct _ExoDieDesktopModel ExoDieDesktopModel;
+
+#define EXO_DIE_TYPE_DESKTOP_MODEL (exo_die_desktop_model_get_type ())
+#define EXO_DIE_DESKTOP_MODEL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EXO_DIE_TYPE_DESKTOP_MODEL, ExoDieDesktopModel))
+#define EXO_DIE_DESKTOP_MODEL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), EXO_DIE_TYPE_DESKTOP_MODEL, ExoDieDesktopModelClass))
+#define EXO_DIE_IS_DESKTOP_MODEL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EXO_DIE_TYPE_DESKTOP_MODEL))
+#define EXO_DIE_IS_DESKTOP_MODEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), EXO_DIE_TYPE_DESKTOP_MODEL))
+#define EXO_DIE_DESKTOP_MODEL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), EXO_DIE_TYPE_DESKTOP_MODEL, ExoDieDesktopModelClass))
+
+/**
+ * ExoDieDesktopModelColumn:
+ * @EXO_DIE_DESKTOP_MODEL_COLUMN_ABSTRACT : the column with the markup text for the renderer.
+ * @EXO_DIE_DESKTOP_MODEL_COLUMN_COMMAND : the column with the application command.
+ * @EXO_DIE_DESKTOP_MODEL_COLUMN_COMMENT : the column with the application comment.
+ * @EXO_DIE_DESKTOP_MODEL_COLUMN_ICON : the column with the application icon.
+ * @EXO_DIE_DESKTOP_MODEL_COLUMN_NAME : the column with the application name.
+ * @EXO_DIE_DESKTOP_MODEL_COLUMN_SNOTIFY : the column with the applications StartupNotify setting.
+ * @EXO_DIE_DESKTOP_MODEL_COLUMN_TERMINAL : the column with the applications Terminal setting.
+ *
+ * The columns provided by the #ExoDieDesktopModel.
+ **/
+typedef enum /*< enum >*/
+{
+ EXO_DIE_DESKTOP_MODEL_COLUMN_ABSTRACT,
+ EXO_DIE_DESKTOP_MODEL_COLUMN_COMMAND,
+ EXO_DIE_DESKTOP_MODEL_COLUMN_COMMENT,
+ EXO_DIE_DESKTOP_MODEL_COLUMN_ICON,
+ EXO_DIE_DESKTOP_MODEL_COLUMN_NAME,
+ EXO_DIE_DESKTOP_MODEL_COLUMN_SNOTIFY,
+ EXO_DIE_DESKTOP_MODEL_COLUMN_TERMINAL,
+ EXO_DIE_DESKTOP_MODEL_N_COLUMNS,
+} ExoDieDesktopModelColumn;
+
+GType exo_die_desktop_model_get_type (void) G_GNUC_CONST;
+
+ExoDieDesktopModel *exo_die_desktop_model_new (void) G_GNUC_MALLOC;
+
+gboolean exo_die_desktop_model_match_func (GtkEntryCompletion *completion,
+ const gchar *key,
+ GtkTreeIter *iter,
+ gpointer user_data);
+
+G_END_DECLS;
+
+#endif /* !__EXO_DIE_DESKTOP_MODEL_H__ */
diff --git a/xfrun/xfrun-dialog.c b/xfrun/xfrun-dialog.c
index beac70a..5ce4ebf 100644
--- a/xfrun/xfrun-dialog.c
+++ b/xfrun/xfrun-dialog.c
@@ -41,6 +41,7 @@
#include "xfrun-dialog.h"
#include "xfrun-history.h"
+#include "exo-die-desktop-model.h"
#define BORDER 8
#define MAX_ENTRIES 20
@@ -94,6 +95,10 @@ static gboolean xfrun_comboboxentry_changed(GtkComboBoxEntry *comboboxentry,
gpointer user_data);
static void xfrun_run_clicked(GtkWidget *widget,
gpointer user_data);
+static gboolean xfrun_dialog_match_selected(GtkEntryCompletion *completion,
+ GtkTreeModel *model,
+ GtkTreeIter *iter,
+ gpointer user_data);
guint __signals[N_SIGS] = { 0, };
@@ -135,7 +140,10 @@ xfrun_dialog_class_init(XfrunDialogClass *klass)
static void
xfrun_dialog_init(XfrunDialog *dialog)
{
+ GtkEntryCompletion *completion;
+ GtkCellRenderer *renderer;
GtkWidget *entry, *comboboxentry, *chk, *btn, *vbox, *bbox;
+ ExoDieDesktopModel *model;
dialog->priv = G_TYPE_INSTANCE_GET_PRIVATE(dialog, XFRUN_TYPE_DIALOG,
XfrunDialogPrivate);
@@ -163,6 +171,28 @@ xfrun_dialog_init(XfrunDialog *dialog)
g_signal_connect(G_OBJECT(comboboxentry), "changed",
G_CALLBACK(xfrun_comboboxentry_changed), dialog);
+ /* allocate a new completion for the entry */
+ completion = gtk_entry_completion_new();
+ gtk_entry_completion_set_inline_completion(completion, TRUE);
+ gtk_entry_completion_set_minimum_key_length(completion, 3);
+ gtk_entry_completion_set_popup_completion(completion, TRUE);
+ g_signal_connect (G_OBJECT (completion), "match-selected", G_CALLBACK (xfrun_dialog_match_selected), dialog);
+ gtk_entry_set_completion(GTK_ENTRY(dialog->priv->entry), completion);
+ g_object_unref (G_OBJECT(completion));
+
+ /* allocate the desktop application model */
+ model = exo_die_desktop_model_new ();
+ gtk_entry_completion_set_match_func (completion, exo_die_desktop_model_match_func, model, NULL);
+ gtk_entry_completion_set_model (completion, GTK_TREE_MODEL (model));
+ g_object_unref (G_OBJECT (model));
+
+ /* add the text renderer */
+ renderer = g_object_new (GTK_TYPE_CELL_RENDERER_TEXT, NULL);
+ gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (completion), renderer, TRUE);
+ gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (completion), renderer,
+ "text", EXO_DIE_DESKTOP_MODEL_COLUMN_COMMAND,
+ NULL);
+
dialog->priv->terminal_chk = chk =
gtk_check_button_new_with_mnemonic(_("Run in _terminal"));
gtk_widget_show(chk);
@@ -540,6 +570,34 @@ xfrun_run_clicked(GtkWidget *widget,
g_strfreev(argv);
}
+static gboolean
+xfrun_dialog_match_selected(GtkEntryCompletion *completion,
+ GtkTreeModel *model,
+ GtkTreeIter *iter,
+ gpointer user_data)
+{
+ XfrunDialog *dialog = XFRUN_DIALOG(user_data);
+ gchar *command;
+ gboolean terminal;
+
+ g_return_val_if_fail(GTK_IS_ENTRY_COMPLETION (completion), FALSE);
+ g_return_val_if_fail(XFRUN_IS_DIALOG (dialog), FALSE);
+ g_return_val_if_fail(GTK_IS_TREE_MODEL (model), FALSE);
+
+ /* determine the attributes for the selected row */
+ gtk_tree_model_get(model, iter,
+ EXO_DIE_DESKTOP_MODEL_COLUMN_COMMAND, &command,
+ EXO_DIE_DESKTOP_MODEL_COLUMN_TERMINAL, &terminal,
+ -1);
+
+ gtk_entry_set_text(GTK_ENTRY(dialog->priv->entry), command);
+ gtk_editable_set_position (GTK_EDITABLE(dialog->priv->entry), -1);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dialog->priv->terminal_chk),
+ terminal);
+
+ return TRUE;
+}
+
GtkWidget *
xfrun_dialog_new(void)
{
More information about the Xfce4-commits
mailing list