[gupnp-tools] av-cp: Provide suggestions for search entry



commit 18c4a5fdfc341cd4d88116105a4836d46d4f12c8
Author: Jens Georg <mail jensge org>
Date:   Sat Jul 18 18:51:03 2020 +0200

    av-cp: Provide suggestions for search entry

 src/av-cp/entry-completion.c | 184 +++++++++++++++++++++++++++++++++++++++++++
 src/av-cp/entry-completion.h |  15 ++++
 src/av-cp/meson.build        |   5 +-
 src/av-cp/search-dialog.c    |   6 ++
 4 files changed, 209 insertions(+), 1 deletion(-)
---
diff --git a/src/av-cp/entry-completion.c b/src/av-cp/entry-completion.c
new file mode 100644
index 0000000..7c9aaba
--- /dev/null
+++ b/src/av-cp/entry-completion.c
@@ -0,0 +1,184 @@
+#include "entry-completion.h"
+
+static void
+entry_completion_constructed (GObject *self);
+
+struct _EntryCompletion
+{
+        GtkEntryCompletion parent_instance;
+
+        GtkListStore *store;
+};
+
+G_DEFINE_TYPE (EntryCompletion, entry_completion, GTK_TYPE_ENTRY_COMPLETION)
+
+enum {
+        PROP_0,
+        N_PROPS
+};
+
+static GParamSpec *properties [N_PROPS];
+
+EntryCompletion *
+entry_completion_new (void)
+{
+        return g_object_new (ENTRY_TYPE_COMPLETION, NULL);
+}
+
+static void
+entry_completion_finalize (GObject *object)
+{
+        G_OBJECT_CLASS (entry_completion_parent_class)->finalize (object);
+}
+
+static void
+entry_completion_get_property (GObject    *object,
+                               guint       prop_id,
+                               GValue     *value,
+                               GParamSpec *pspec)
+{
+        switch (prop_id)
+        {
+                default:
+                        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+        }
+}
+
+static void
+entry_completion_set_property (GObject      *object,
+                               guint         prop_id,
+                               const GValue *value,
+                               GParamSpec   *pspec)
+{
+    switch (prop_id)
+      {
+      default:
+        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      }
+}
+
+static gboolean
+entry_completion_on_match_selected (GtkEntryCompletion *self, GtkTreeModel *model, GtkTreeIter *iter, 
gpointer user_data)
+{
+        char *match = NULL;
+        GtkEntry *entry = gtk_entry_completion_get_entry (self);
+        gtk_tree_model_get (model, iter, 0, &match, -1);
+        
+        char *old_text = g_strdup (gtk_entry_get_text (entry));
+        char *needle = strrchr (old_text, ' ');
+        if (needle != NULL) {
+                needle++;
+                *needle = '\0';
+        }
+
+        char *new_text = g_strconcat (needle == NULL ? "" : old_text, match, NULL);
+        gtk_entry_set_text (entry, new_text);
+        gtk_editable_set_position (GTK_EDITABLE (entry), strlen (new_text));
+        g_free (new_text);
+        g_free (old_text);
+
+        return TRUE;
+}
+
+static void
+entry_completion_class_init (EntryCompletionClass *klass)
+{
+        GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+        object_class->constructed = entry_completion_constructed;
+        object_class->finalize = entry_completion_finalize;
+        object_class->get_property = entry_completion_get_property;
+        object_class->set_property = entry_completion_set_property;
+
+        GtkEntryCompletionClass *entry_class = GTK_ENTRY_COMPLETION_CLASS (klass);
+        entry_class->match_selected = entry_completion_on_match_selected;
+}
+
+static gboolean
+entry_completion_on_match (GtkEntryCompletion *self, const char *key, GtkTreeIter *iter, gpointer user_data)
+{
+        GtkTreeModel *model = gtk_entry_completion_get_model (self);
+        char *candidate;
+        GtkEntry *entry = gtk_entry_completion_get_entry (self);
+        gboolean retval = FALSE;
+
+        gtk_tree_model_get (model, iter, 0, &candidate, -1);
+
+        char *needle = strrchr (key, ' ');
+        if (needle != NULL) {
+                if ((needle - key)> gtk_editable_get_position (GTK_EDITABLE (entry))) {
+                        g_print ("Position wrong\n");
+                        goto out;
+                }
+                needle++;
+        } else {
+                needle = key;
+        }
+
+        if (strlen(needle) == 0) {
+                goto out;
+        }
+
+        retval = g_str_has_prefix (candidate, needle);
+
+out:
+        g_free (candidate);
+
+        return retval;
+}
+
+static void
+entry_completion_init (EntryCompletion *self)
+{
+}
+
+static void
+entry_completion_constructed (GObject *object)
+{
+        EntryCompletion *self = ENTRY_COMPLETION (object);
+
+        if (G_OBJECT_CLASS (entry_completion_parent_class)->constructed != NULL) {
+                G_OBJECT_CLASS (entry_completion_parent_class)->constructed (G_OBJECT (self));
+        }
+
+        gtk_entry_completion_set_match_func (GTK_ENTRY_COMPLETION (self),
+                                             entry_completion_on_match,
+                                             NULL,
+                                             NULL);
+
+        self->store = gtk_list_store_new (1, G_TYPE_STRING);
+
+        gtk_entry_completion_set_model (GTK_ENTRY_COMPLETION (self), GTK_TREE_MODEL (self->store));
+        gtk_entry_completion_set_text_column (GTK_ENTRY_COMPLETION (self), 0);
+}
+
+void
+entry_completion_set_search_criteria (EntryCompletion *self, char** criteria)
+{
+        GtkTreeIter iter;
+        gtk_list_store_clear (self->store);
+        // Prefill ListStore with the search expression keywords
+        gtk_list_store_insert_with_values (self->store, &iter, -1,
+                0, "and", -1);
+        gtk_list_store_insert_with_values (self->store, &iter, -1,
+                0, "or", -1);
+        gtk_list_store_insert_with_values (self->store, &iter, -1,
+                0, "contains", -1);
+        gtk_list_store_insert_with_values (self->store, &iter, -1,
+                0, "doesNotContain", -1);
+        gtk_list_store_insert_with_values (self->store, &iter, -1,
+                0, "derivedFrom", -1);
+        gtk_list_store_insert_with_values (self->store, &iter, -1,
+                0, "exists", -1);
+        gtk_list_store_insert_with_values (self->store, &iter, -1,
+                0, "true", -1);
+        gtk_list_store_insert_with_values (self->store, &iter, -1,
+                0, "false", -1);
+
+        char **it = criteria;
+        while (*it != NULL) {
+                gtk_list_store_insert_with_values (self->store, &iter, -1,
+                        0, *it, -1);
+                it++;
+        }
+}
\ No newline at end of file
diff --git a/src/av-cp/entry-completion.h b/src/av-cp/entry-completion.h
new file mode 100644
index 0000000..f999c41
--- /dev/null
+++ b/src/av-cp/entry-completion.h
@@ -0,0 +1,15 @@
+#pragma once
+
+#include <glib-object.h>
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define ENTRY_TYPE_COMPLETION (entry_completion_get_type())
+
+G_DECLARE_FINAL_TYPE (EntryCompletion, entry_completion, ENTRY, COMPLETION, GtkEntryCompletion)
+
+EntryCompletion *entry_completion_new (void);
+void entry_completion_set_search_criteria (EntryCompletion *self, char** criteria);
+
+G_END_DECLS
diff --git a/src/av-cp/meson.build b/src/av-cp/meson.build
index 00bf411..f0a62e7 100644
--- a/src/av-cp/meson.build
+++ b/src/av-cp/meson.build
@@ -25,7 +25,10 @@ executable('gupnp-av-cp',
             'search-dialog.c',
             'search-dialog.h',
             'server-device.c',
-            'server-device.h'] + av_cp_resources + av_cp_win_resources,
+            'server-device.h',
+            'entry-completion.c',
+            'entry-completion.h'
+            ] + av_cp_resources + av_cp_win_resources,
            dependencies : [gupnp, gssdp, gupnp_av, gtk, util, gtksourceview, config_header],
            export_dynamic : true,
            install : true,
diff --git a/src/av-cp/search-dialog.c b/src/av-cp/search-dialog.c
index d929cc5..c61bb78 100644
--- a/src/av-cp/search-dialog.c
+++ b/src/av-cp/search-dialog.c
@@ -23,6 +23,7 @@
 
 #include <string.h>
 
+#include "entry-completion.h"
 #include "search-dialog.h"
 #include "server-device.h"
 #include "didl-dialog.h"
@@ -376,6 +377,7 @@ search_dialog_init (SearchDialog *self)
         priv = search_dialog_get_instance_private (self);
 
         priv->parser = gupnp_search_criteria_parser_new ();
+        gtk_entry_set_completion (priv->search_dialog_entry, entry_completion_new ());
 
         GMenu *menu = g_menu_new ();
         g_menu_insert (menu, 0, _("Show _DIDL…"), "search.show-didl");
@@ -486,8 +488,12 @@ void
 search_dialog_set_server (SearchDialog *self, AVCPMediaServer *server)
 {
         SearchDialogPrivate *priv = search_dialog_get_instance_private (self);
+        GtkEntryCompletion *completion = gtk_entry_get_completion (priv->search_dialog_entry);
 
         priv->server = server;
+
+        entry_completion_set_search_criteria (ENTRY_COMPLETION (completion),
+                                              av_cp_media_server_get_search_caps (server));
 }
 
 void


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