[gnome-builder/wip/search] search: implement new search design
- From: Christian Hergert <chergert src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-builder/wip/search] search: implement new search design
- Date: Sat, 17 Jan 2015 23:12:17 +0000 (UTC)
commit d86dd7103ed4ef4f88af1264b961c866fad290f1
Author: Christian Hergert <christian hergert me>
Date: Sat Jan 17 15:09:09 2015 -0800
search: implement new search design
**This is not ready for general consumption**
The new search design was laid out on the Builder mailing list[1]. This
is the beginning of that implementation.
## Pending Work
* Keyboard navigation is not solved
* "X More" result navigation is not implemented
* We need a plaholder page when no search is performed
* Additional search providers.
* Connecting activation signals
* etc ...
## Performance Notes
In particular, the old search felt slow for a couple of reasons.
* We inserted delays to slow down search which were too large.
Well at least when the search layer is faster.
* We created way too many result objects. Since these were also
GtkWidgets, the overhead was huge.
* We were adding these widgets and sorting them in GtkListstore,
which was not meant to handle the load. realize() costs alone
where too much.
So this adds a new structure called GbSearchReducer. It's purpose is to
help us avoid creating objects if we know they will simply be discarded
based on the expected number of results. Since most search layers will
calculate the score as they walk through the result set, all we need is
the score to determine if we need to inflate a full result object.
This alone makes search much faster when walking through the Git trie.
[1] https://mail.gnome.org/archives/builder-list/2015-January/msg00002.html
src/git/gb-git-search-provider.c | 89 +++---
src/git/gb-git-search-provider.h | 4 +-
src/git/gb-git-search-result.c | 224 ------------
src/git/gb-git-search-result.h | 55 ---
src/gnome-builder.mk | 6 +-
src/resources/css/builder.Adwaita.css | 9 +
src/resources/gnome-builder.gresource.xml | 3 +-
src/resources/ui/gb-git-search-result.ui | 48 ---
src/resources/ui/gb-search-box.ui | 12 +-
src/resources/ui/gb-search-display-group.ui | 59 ++++
src/resources/ui/gb-search-display.ui | 27 --
src/search/gb-search-box.c | 11 +-
src/search/gb-search-context.c | 275 +++++----------
src/search/gb-search-context.h | 43 ++-
src/search/gb-search-display-group.c | 457 ++++++++++++++++++++++++
src/search/gb-search-display-group.h | 62 ++++
src/search/gb-search-display.c | 500 +++++++++++++++++----------
src/search/gb-search-display.h | 16 +-
src/search/gb-search-manager.c | 131 +++-----
src/search/gb-search-manager.h | 26 +-
src/search/gb-search-provider.c | 127 ++++++--
src/search/gb-search-provider.h | 51 ++-
src/search/gb-search-reducer.c | 98 ++++++
src/search/gb-search-reducer.h | 49 +++
src/search/gb-search-result.c | 128 ++++---
src/search/gb-search-result.h | 25 +-
src/search/gb-search-types.h | 45 ++-
27 files changed, 1545 insertions(+), 1035 deletions(-)
---
diff --git a/src/git/gb-git-search-provider.c b/src/git/gb-git-search-provider.c
index dc0846c..bec6d76 100644
--- a/src/git/gb-git-search-provider.c
+++ b/src/git/gb-git-search-provider.c
@@ -23,9 +23,8 @@
#include "fuzzy.h"
#include "gb-git-search-provider.h"
-#include "gb-git-search-result.h"
-#include "gb-log.h"
#include "gb-search-context.h"
+#include "gb-search-reducer.h"
#include "gb-search-result.h"
#define GB_GIT_SEARCH_PROVIDER_MAX_MATCHES 1000
@@ -38,15 +37,9 @@ struct _GbGitSearchProviderPrivate
gchar *repository_shorthand;
};
-static void search_provider_init (GbSearchProviderInterface *iface);
-
-G_DEFINE_TYPE_EXTENDED (GbGitSearchProvider,
- gb_git_search_provider,
- G_TYPE_OBJECT,
- 0,
- G_ADD_PRIVATE (GbGitSearchProvider)
- G_IMPLEMENT_INTERFACE (GB_TYPE_SEARCH_PROVIDER,
- search_provider_init))
+G_DEFINE_TYPE_WITH_PRIVATE (GbGitSearchProvider,
+ gb_git_search_provider,
+ GB_TYPE_SEARCH_PROVIDER)
enum {
PROP_0,
@@ -112,8 +105,6 @@ gb_git_search_provider_build_file_index (GTask *task,
guint count;
guint i;
- ENTRY;
-
g_return_if_fail (G_IS_FILE (repository_dir));
/*
@@ -131,7 +122,7 @@ gb_git_search_provider_build_file_index (GTask *task,
if (!repository)
{
g_task_return_error (task, error);
- GOTO (cleanup);
+ goto cleanup;
}
ref = ggit_repository_get_head (repository, NULL);
@@ -152,7 +143,7 @@ gb_git_search_provider_build_file_index (GTask *task,
if (!index)
{
g_task_return_error (task, error);
- GOTO (cleanup);
+ goto cleanup;
}
entries = ggit_index_get_entries (index);
@@ -196,8 +187,6 @@ cleanup:
g_clear_pointer (&entries, ggit_index_entries_unref);
g_clear_object (&index);
g_clear_object (&repository);
-
- EXIT;
}
static gchar *
@@ -243,10 +232,11 @@ split_path (const gchar *path,
static void
gb_git_search_provider_populate (GbSearchProvider *provider,
GbSearchContext *context,
+ const gchar *search_terms,
+ gsize max_results,
GCancellable *cancellable)
{
GbGitSearchProvider *self = (GbGitSearchProvider *)provider;
- GList *list = NULL;
g_return_if_fail (GB_IS_GIT_SEARCH_PROVIDER (self));
g_return_if_fail (GB_IS_SEARCH_CONTEXT (context));
@@ -255,14 +245,13 @@ gb_git_search_provider_populate (GbSearchProvider *provider,
if (self->priv->file_index)
{
GString *str = g_string_new (NULL);
- const gchar *search_text;
+ GbSearchReducer reducer = { 0 };
gchar *delimited;
GArray *matches;
guint i;
guint truncate_len;
- search_text = gb_search_context_get_search_text (context);
- delimited = remove_spaces (search_text);
+ delimited = remove_spaces (search_terms);
matches = fuzzy_match (self->priv->file_index, delimited,
GB_GIT_SEARCH_PROVIDER_MAX_MATCHES);
@@ -301,38 +290,38 @@ gb_git_search_provider_populate (GbSearchProvider *provider,
truncate_len = str->len;
+ gb_search_reducer_init (&reducer, context, provider);
+
for (i = 0; i < matches->len; i++)
{
FuzzyMatch *match;
- GtkWidget *widget;
gchar *shortname = NULL;
gchar **parts;
guint j;
match = &g_array_index (matches, FuzzyMatch, i);
- parts = split_path (match->value, &shortname);
- for (j = 0; parts [j]; j++)
- g_string_append_printf (str, " / %s", parts [j]);
-
- /* TODO: Make a git file search result */
- widget = g_object_new (GB_TYPE_GIT_SEARCH_RESULT,
- "visible", TRUE,
- "score", match->score,
- "repository-name", str->str,
- "path", match->value,
- "display-name", shortname,
- NULL);
- list = g_list_prepend (list, widget);
-
- g_free (shortname);
- g_strfreev (parts);
- g_string_truncate (str, truncate_len);
+ if (gb_search_reducer_accepts (&reducer, match->score))
+ {
+ GbSearchResult *result;
+
+ parts = split_path (match->value, &shortname);
+ for (j = 0; parts [j]; j++)
+ g_string_append_printf (str, " / %s", parts [j]);
+
+ result = gb_search_result_new (match->value, match->score);
+ gb_search_reducer_push (&reducer, result);
+ g_object_unref (result);
+
+ g_free (shortname);
+ g_strfreev (parts);
+ g_string_truncate (str, truncate_len);
+ }
}
- list = g_list_reverse (list);
- gb_search_context_add_results (context, provider, list, TRUE);
+ gb_search_context_set_provider_count (context, provider, matches->len);
+ gb_search_reducer_destroy (&reducer);
g_array_unref (matches);
g_free (delimited);
g_string_free (str, TRUE);
@@ -380,6 +369,14 @@ gb_git_search_provider_set_repository (GbGitSearchProvider *provider,
}
}
+const gchar *
+gb_git_search_provider_get_verb (GbSearchProvider *provider)
+{
+ g_return_val_if_fail (GB_IS_GIT_SEARCH_PROVIDER (provider), NULL);
+
+ return _("Switch To");
+}
+
static void
gb_git_search_provider_finalize (GObject *object)
{
@@ -435,11 +432,15 @@ static void
gb_git_search_provider_class_init (GbGitSearchProviderClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GbSearchProviderClass *provider_class = GB_SEARCH_PROVIDER_CLASS (klass);
object_class->finalize = gb_git_search_provider_finalize;
object_class->get_property = gb_git_search_provider_get_property;
object_class->set_property = gb_git_search_provider_set_property;
+ provider_class->populate = gb_git_search_provider_populate;
+ provider_class->get_verb = gb_git_search_provider_get_verb;
+
/**
* GbGitSearchProvider:repository:
*
@@ -463,9 +464,3 @@ gb_git_search_provider_init (GbGitSearchProvider *self)
{
self->priv = gb_git_search_provider_get_instance_private (self);
}
-
-static void
-search_provider_init (GbSearchProviderInterface *iface)
-{
- iface->populate = gb_git_search_provider_populate;
-}
diff --git a/src/git/gb-git-search-provider.h b/src/git/gb-git-search-provider.h
index 882b924..b330710 100644
--- a/src/git/gb-git-search-provider.h
+++ b/src/git/gb-git-search-provider.h
@@ -40,7 +40,7 @@ typedef struct _GbGitSearchProviderPrivate GbGitSearchProviderPrivate;
struct _GbGitSearchProvider
{
- GObject parent;
+ GbSearchProvider parent;
/*< private >*/
GbGitSearchProviderPrivate *priv;
@@ -48,7 +48,7 @@ struct _GbGitSearchProvider
struct _GbGitSearchProviderClass
{
- GObjectClass parent;
+ GbSearchProviderClass parent;
};
GType gb_git_search_provider_get_type (void);
diff --git a/src/gnome-builder.mk b/src/gnome-builder.mk
index 35af49c..b1df7df 100644
--- a/src/gnome-builder.mk
+++ b/src/gnome-builder.mk
@@ -113,8 +113,6 @@ libgnome_builder_la_SOURCES = \
src/gedit/gedit-menu-stack-switcher.h \
src/git/gb-git-search-provider.c \
src/git/gb-git-search-provider.h \
- src/git/gb-git-search-result.c \
- src/git/gb-git-search-result.h \
src/html/gb-html-completion-provider.c \
src/html/gb-html-completion-provider.h \
src/html/gb-html-document.c \
@@ -151,10 +149,14 @@ libgnome_builder_la_SOURCES = \
src/search/gb-search-context.h \
src/search/gb-search-display.c \
src/search/gb-search-display.h \
+ src/search/gb-search-display-group.c \
+ src/search/gb-search-display-group.h \
src/search/gb-search-manager.c \
src/search/gb-search-manager.h \
src/search/gb-search-provider.c \
src/search/gb-search-provider.h \
+ src/search/gb-search-reducer.c \
+ src/search/gb-search-reducer.h \
src/search/gb-search-result.c \
src/search/gb-search-result.h \
src/search/gb-search-types.h \
diff --git a/src/resources/css/builder.Adwaita.css b/src/resources/css/builder.Adwaita.css
index 44742c6..55f3433 100644
--- a/src/resources/css/builder.Adwaita.css
+++ b/src/resources/css/builder.Adwaita.css
@@ -200,3 +200,12 @@ GbSourceStyleSchemeWidget GtkSourceView {
GtkScrolledWindow.gb-linked-scroller {
border-top: none;
}
+
+
+/*
+ * Rows inside of global search. They have a really confusing transition
+ * by default.
+ */
+GbSearchDisplayGroup GtkListBox .list-row {
+ transition: none;
+}
diff --git a/src/resources/gnome-builder.gresource.xml b/src/resources/gnome-builder.gresource.xml
index 255e6a8..2c564b3 100644
--- a/src/resources/gnome-builder.gresource.xml
+++ b/src/resources/gnome-builder.gresource.xml
@@ -42,7 +42,6 @@
<file>ui/gb-editor-tweak-widget.ui</file>
<file>ui/gb-editor-view.ui</file>
<file>ui/gb-editor-workspace.ui</file>
- <file>ui/gb-git-search-result.ui</file>
<file>ui/gb-html-view.ui</file>
<file>ui/gb-preferences-window.ui</file>
<file>ui/gb-preferences-page-editor.ui</file>
@@ -50,7 +49,7 @@
<file>ui/gb-preferences-page-language.ui</file>
<file>ui/gb-preferences-page-vim.ui</file>
<file>ui/gb-search-box.ui</file>
- <file>ui/gb-search-display.ui</file>
+ <file>ui/gb-search-display-group.ui</file>
<file>ui/gb-workbench.ui</file>
</gresource>
</gresources>
diff --git a/src/resources/ui/gb-search-box.ui b/src/resources/ui/gb-search-box.ui
index 0c4ceed..5d9f52a 100644
--- a/src/resources/ui/gb-search-box.ui
+++ b/src/resources/ui/gb-search-box.ui
@@ -35,8 +35,16 @@
<property name="modal">false</property>
<property name="relative-to">entry</property>
<child>
- <object class="GbSearchDisplay" id="display">
- <property name="visible">true</property>
+ <object class="GbScrolledWindow" id="scroller">
+ <property name="max-content-height">800</property>
+ <property name="min-content-height">300</property>
+ <property name="min-content-width">750</property>
+ <property name="visible">True</property>
+ <child>
+ <object class="GbSearchDisplay" id="display">
+ <property name="visible">true</property>
+ </object>
+ </child>
</object>
</child>
</object>
diff --git a/src/resources/ui/gb-search-display-group.ui b/src/resources/ui/gb-search-display-group.ui
new file mode 100644
index 0000000..cf93be8
--- /dev/null
+++ b/src/resources/ui/gb-search-display-group.ui
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <!-- interface-requires gtk+ 3.8 -->
+ <template class="GbSearchDisplayGroup" parent="GtkBox">
+ <property name="orientation">GTK_ORIENTATION_HORIZONTAL</property>
+ <child>
+ <object class="GtkLabel" id="label">
+ <property name="expand">False</property>
+ <property name="visible">True</property>
+ <property name="xalign">1.0</property>
+ <property name="yalign">0.0</property>
+ <property name="xpad">6</property>
+ <property name="ypad">3</property>
+ <property name="margin_start">24</property>
+ <style>
+ <class name="dim-label"/>
+ </style>
+ </object>
+ </child>
+ <child>
+ <object class="GtkSeparator">
+ <property name="orientation">GTK_ORIENTATION_VERTICAL</property>
+ <property name="visible">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkBox">
+ <property name="orientation">GTK_ORIENTATION_VERTICAL</property>
+ <property name="visible">True</property>
+ <style>
+ <class name="view"/>
+ </style>
+ <child>
+ <object class="GtkListBox" id="rows">
+ <property name="hexpand">True</property>
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkListBoxRow" id="more_row">
+ <child>
+ <object class="GtkLabel" id="more_label">
+ <property name="visible">True</property>
+ <property name="halign">end</property>
+ <property name="hexpand">False</property>
+ <property name="use-markup">True</property>
+ <property name="ypad">3</property>
+ <property name="xpad">6</property>
+ <style>
+ <class name="dim-label"/>
+ </style>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </template>
+</interface>
diff --git a/src/search/gb-search-box.c b/src/search/gb-search-box.c
index ba66582..e468daa 100644
--- a/src/search/gb-search-box.c
+++ b/src/search/gb-search-box.c
@@ -21,14 +21,17 @@
#include <glib/gi18n.h>
#include "gb-glib.h"
+#include "gb-scrolled-window.h"
#include "gb-search-box.h"
+#include "gb-search-context.h"
#include "gb-search-display.h"
#include "gb-search-manager.h"
+#include "gb-search-result.h"
#include "gb-widget.h"
#include "gb-workbench.h"
-#define SHORT_DELAY_TIMEOUT_MSEC 250
-#define LONG_DELAY_TIMEOUT_MSEC 500
+#define SHORT_DELAY_TIMEOUT_MSEC 20
+#define LONG_DELAY_TIMEOUT_MSEC 250
struct _GbSearchBoxPrivate
{
@@ -113,8 +116,9 @@ gb_search_box_delay_cb (gpointer user_data)
if (!search_text)
return G_SOURCE_REMOVE;
- context = gb_search_manager_search (box->priv->search_manager, search_text);
+ context = gb_search_manager_search (box->priv->search_manager, NULL, search_text); /* TODO: Remove search
text */
gb_search_display_set_context (box->priv->display, context);
+ gb_search_context_execute (context, search_text);
g_object_unref (context);
return G_SOURCE_REMOVE;
@@ -448,6 +452,7 @@ gb_search_box_class_init (GbSearchBoxClass *klass)
GB_WIDGET_CLASS_BIND (klass, GbSearchBox, popover);
g_type_ensure (GB_TYPE_SEARCH_DISPLAY);
+ g_type_ensure (GB_TYPE_SCROLLED_WINDOW);
}
static void
diff --git a/src/search/gb-search-context.c b/src/search/gb-search-context.c
index 04a0798..8281a9c 100644
--- a/src/search/gb-search-context.c
+++ b/src/search/gb-search-context.c
@@ -1,6 +1,6 @@
/* gb-search-context.c
*
- * Copyright (C) 2014 Christian Hergert <christian hergert me>
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
*
* 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
@@ -16,11 +16,9 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#define G_LOG_DOMAIN "search-context"
-
#include <glib/gi18n.h>
+#include <gio/gio.h>
-#include "gb-log.h"
#include "gb-search-context.h"
#include "gb-search-provider.h"
#include "gb-search-result.h"
@@ -29,8 +27,6 @@ struct _GbSearchContextPrivate
{
GCancellable *cancellable;
GList *providers;
- gchar *search_text;
- GList *results;
guint executed : 1;
};
@@ -39,183 +35,114 @@ G_DEFINE_TYPE_WITH_PRIVATE (GbSearchContext, gb_search_context, G_TYPE_OBJECT)
enum {
PROP_0,
PROP_PROVIDERS,
- PROP_SEARCH_TEXT,
LAST_PROP
};
enum {
- RESULTS_ADDED,
+ COUNT_SET,
+ RESULT_ADDED,
+ RESULT_REMOVED,
LAST_SIGNAL
};
-static guint gSignals [LAST_SIGNAL];
static GParamSpec *gParamSpecs [LAST_PROP];
+static guint gSignals [LAST_SIGNAL];
-/**
- * gb_search_context_new:
- * @providers: (element-type GbSearchProvider*) (transfer none): A #GList
- *
- * Creates a new search context with the provided search providers.
- *
- * Returns: (transfer full): A newly allocated #GbSearchContext.
- */
GbSearchContext *
-gb_search_context_new (const GList *providers,
- const gchar *search_text)
+gb_search_context_new (void)
{
- return g_object_new (GB_TYPE_SEARCH_CONTEXT,
- "providers", providers,
- "search-text", search_text,
- NULL);
+ return g_object_new (GB_TYPE_SEARCH_CONTEXT, NULL);
}
-void
-gb_search_context_execute (GbSearchContext *context)
-{
- GbSearchContextPrivate *priv;
- GList *iter;
-
- g_return_if_fail (GB_IS_SEARCH_CONTEXT (context));
-
- priv = context->priv;
-
- if (priv->executed)
- {
- g_warning ("GbSearchContext has already been executed.");
- return;
- }
-
- priv->executed = 1;
-
- for (iter = priv->providers; iter; iter = iter->next)
- gb_search_provider_populate (iter->data, context, priv->cancellable);
-}
-
-/**
- * gb_search_context_get_cancellable:
- * @context: A #GbSearchContext
- *
- * Retrieves the cancellable to cancel the search request. If the search has
- * completed, this will return NULL.
- *
- * Returns: (transfer none): A #GCancellable or %NULL.
- */
-GCancellable *
-gb_search_context_get_cancellable (GbSearchContext *context)
+const GList *
+gb_search_context_get_providers (GbSearchContext *context)
{
g_return_val_if_fail (GB_IS_SEARCH_CONTEXT (context), NULL);
- return context->priv->cancellable;
+ return context->priv->providers;
}
void
-gb_search_context_cancel (GbSearchContext *context)
+gb_search_context_add_result (GbSearchContext *context,
+ GbSearchProvider *provider,
+ GbSearchResult *result)
{
g_return_if_fail (GB_IS_SEARCH_CONTEXT (context));
+ g_return_if_fail (GB_IS_SEARCH_PROVIDER (provider));
+ g_return_if_fail (GB_IS_SEARCH_RESULT (result));
- g_cancellable_cancel (context->priv->cancellable);
+ g_signal_emit (context, gSignals [RESULT_ADDED], 0, provider, result);
}
-/**
- * gb_search_context_get_results:
- * @context: A #GbSearchContext
- *
- * Fetches the current results.
- *
- * Returns: (transfer none): A #GList of current results.
- */
-const GList *
-gb_search_context_get_results (GbSearchContext *context)
+void
+gb_search_context_remove_result (GbSearchContext *context,
+ GbSearchProvider *provider,
+ GbSearchResult *result)
{
- g_return_val_if_fail (GB_IS_SEARCH_CONTEXT (context), NULL);
+ g_return_if_fail (GB_IS_SEARCH_CONTEXT (context));
+ g_return_if_fail (GB_IS_SEARCH_PROVIDER (provider));
+ g_return_if_fail (GB_IS_SEARCH_RESULT (result));
- return context->priv->results;
+ g_signal_emit (context, gSignals [RESULT_REMOVED], 0, provider, result);
}
-static void
-gb_search_context_results_added (GbSearchContext *context,
- GbSearchProvider *provider,
- GList *results,
- gboolean finished)
+void
+gb_search_context_set_provider_count (GbSearchContext *context,
+ GbSearchProvider *provider,
+ guint64 count)
{
- ENTRY;
-
g_return_if_fail (GB_IS_SEARCH_CONTEXT (context));
g_return_if_fail (GB_IS_SEARCH_PROVIDER (provider));
- /* TODO: how should we deal with priority? */
-
- context->priv->results = g_list_concat (context->priv->results, results);
-
- EXIT;
+ g_signal_emit (context, gSignals [COUNT_SET], 0, provider, count);
}
-/**
- * gb_search_context_add_results:
- * @results: (transfer full) (element-type GbSearchResult*): A #GList or %NULL
- * @finished: if the provider is finished adding results.
- *
- * This function will add a list of results to the context. Ownership of
- * @results and the contained elements will be transfered to @context.
- */
void
-gb_search_context_add_results (GbSearchContext *context,
- GbSearchProvider *provider,
- GList *results,
- gboolean finished)
+gb_search_context_execute (GbSearchContext *context,
+ const gchar *search_terms)
{
- ENTRY;
+ GList *iter;
g_return_if_fail (GB_IS_SEARCH_CONTEXT (context));
- g_return_if_fail (GB_IS_SEARCH_PROVIDER (provider));
+ g_return_if_fail (!context->priv->executed);
+ g_return_if_fail (search_terms);
- g_list_foreach (results, (GFunc)g_object_ref_sink, NULL);
+ context->priv->executed = TRUE;
- g_signal_emit (context, gSignals [RESULTS_ADDED], 0,
- provider, results, finished);
+ for (iter = context->priv->providers; iter; iter = iter->next)
+ {
+ gsize max_results = 0;
- EXIT;
-}
+ /* TODO: Get the max results for this provider */
-const gchar *
-gb_search_context_get_search_text (GbSearchContext *context)
-{
- g_return_val_if_fail (GB_IS_SEARCH_CONTEXT (context), NULL);
-
- return context->priv->search_text;
+ gb_search_provider_populate (iter->data,
+ context,
+ search_terms,
+ max_results,
+ context->priv->cancellable);
+ }
}
-static void
-gb_search_context_set_search_text (GbSearchContext *context,
- const gchar *search_text)
+void
+gb_search_context_cancel (GbSearchContext *context)
{
g_return_if_fail (GB_IS_SEARCH_CONTEXT (context));
- g_return_if_fail (search_text);
- if (search_text != context->priv->search_text)
- {
- g_free (context->priv->search_text);
- context->priv->search_text = g_strdup (search_text);
- g_object_notify_by_pspec (G_OBJECT (context),
- gParamSpecs [PROP_SEARCH_TEXT]);
- }
+ if (!g_cancellable_is_cancelled (context->priv->cancellable))
+ g_cancellable_cancel (context->priv->cancellable);
}
-static void
-gb_search_context_set_providers (GbSearchContext *context,
- const GList *providers)
+void
+gb_search_context_add_provider (GbSearchContext *context,
+ GbSearchProvider *provider,
+ gsize max_results)
{
- GbSearchContextPrivate *priv;
-
g_return_if_fail (GB_IS_SEARCH_CONTEXT (context));
+ g_return_if_fail (GB_IS_SEARCH_PROVIDER (provider));
+ g_return_if_fail (!context->priv->executed);
- priv = context->priv;
-
- g_list_foreach (priv->providers, (GFunc)g_object_unref, NULL);
- g_list_free (priv->providers);
-
- priv->providers = g_list_copy ((GList *)providers);
- g_list_foreach (priv->providers, (GFunc)g_object_ref, NULL);
+ context->priv->providers = g_list_append (context->priv->providers,
+ g_object_ref (provider));
}
static void
@@ -223,13 +150,10 @@ gb_search_context_finalize (GObject *object)
{
GbSearchContextPrivate *priv = GB_SEARCH_CONTEXT (object)->priv;
- g_list_foreach (priv->providers, (GFunc)g_object_unref, NULL);
- g_clear_pointer (&priv->providers, g_list_free);
-
- g_list_foreach (priv->results, (GFunc)g_object_unref, NULL);
- g_clear_pointer (&priv->results, g_list_free);
+ g_clear_object (&priv->cancellable);
- g_clear_pointer (&priv->search_text, g_free);
+ g_list_foreach (priv->providers, (GFunc)g_object_unref, NULL);
+ g_list_free (priv->providers);
G_OBJECT_CLASS (gb_search_context_parent_class)->finalize (object);
}
@@ -244,10 +168,6 @@ gb_search_context_get_property (GObject *object,
switch (prop_id)
{
- case PROP_SEARCH_TEXT:
- g_value_set_string (value, gb_search_context_get_search_text (self));
- break;
-
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
@@ -263,14 +183,6 @@ gb_search_context_set_property (GObject *object,
switch (prop_id)
{
- case PROP_PROVIDERS:
- gb_search_context_set_providers (self, g_value_get_pointer (value));
- break;
-
- case PROP_SEARCH_TEXT:
- gb_search_context_set_search_text (self, g_value_get_string (value));
- break;
-
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
@@ -285,48 +197,49 @@ gb_search_context_class_init (GbSearchContextClass *klass)
object_class->get_property = gb_search_context_get_property;
object_class->set_property = gb_search_context_set_property;
- klass->results_added = gb_search_context_results_added;
-
- gParamSpecs [PROP_SEARCH_TEXT] =
- g_param_spec_string ("search-text",
- _("Search Text"),
- _("The search text for the context."),
- NULL,
- (G_PARAM_READWRITE |
- G_PARAM_CONSTRUCT_ONLY |
- G_PARAM_STATIC_STRINGS));
- g_object_class_install_property (object_class, PROP_SEARCH_TEXT,
- gParamSpecs [PROP_SEARCH_TEXT]);
-
- gParamSpecs [PROP_PROVIDERS] =
- g_param_spec_pointer ("providers",
- _("Providers"),
- _("The providers for the search context."),
- (G_PARAM_READWRITE |
- G_PARAM_CONSTRUCT_ONLY |
- G_PARAM_STATIC_STRINGS));
- g_object_class_install_property (object_class, PROP_PROVIDERS,
- gParamSpecs [PROP_PROVIDERS]);
-
- gSignals [RESULTS_ADDED] =
- g_signal_new ("results-added",
- GB_TYPE_SEARCH_CONTEXT,
+ gSignals [COUNT_SET] =
+ g_signal_new ("count-set",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ 0,
+ NULL,
+ NULL,
+ g_cclosure_marshal_generic,
+ G_TYPE_NONE,
+ 2,
+ GB_TYPE_SEARCH_PROVIDER,
+ G_TYPE_UINT64);
+
+ gSignals [RESULT_ADDED] =
+ g_signal_new ("result-added",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ 0,
+ NULL,
+ NULL,
+ g_cclosure_marshal_generic,
+ G_TYPE_NONE,
+ 2,
+ GB_TYPE_SEARCH_PROVIDER,
+ GB_TYPE_SEARCH_RESULT);
+
+ gSignals [RESULT_REMOVED] =
+ g_signal_new ("result-removed",
+ G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
- G_STRUCT_OFFSET (GbSearchContextClass, results_added),
+ 0,
NULL,
NULL,
g_cclosure_marshal_generic,
G_TYPE_NONE,
- 3,
+ 2,
GB_TYPE_SEARCH_PROVIDER,
- G_TYPE_POINTER,
- G_TYPE_BOOLEAN);
+ GB_TYPE_SEARCH_RESULT);
}
static void
gb_search_context_init (GbSearchContext *self)
{
- ENTRY;
self->priv = gb_search_context_get_instance_private (self);
- EXIT;
+ self->priv->cancellable = g_cancellable_new ();
}
diff --git a/src/search/gb-search-context.h b/src/search/gb-search-context.h
index 5aae19a..688bc1e 100644
--- a/src/search/gb-search-context.h
+++ b/src/search/gb-search-context.h
@@ -1,6 +1,6 @@
/* gb-search-context.h
*
- * Copyright (C) 2014 Christian Hergert <christian hergert me>
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
*
* 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
@@ -19,13 +19,12 @@
#ifndef GB_SEARCH_CONTEXT_H
#define GB_SEARCH_CONTEXT_H
-#include <gio/gio.h>
+#include <glib-object.h>
#include "gb-search-types.h"
G_BEGIN_DECLS
-#define GB_TYPE_SEARCH_CONTEXT (gb_search_context_get_type())
#define GB_SEARCH_CONTEXT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GB_TYPE_SEARCH_CONTEXT,
GbSearchContext))
#define GB_SEARCH_CONTEXT_CONST(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GB_TYPE_SEARCH_CONTEXT,
GbSearchContext const))
#define GB_SEARCH_CONTEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GB_TYPE_SEARCH_CONTEXT,
GbSearchContextClass))
@@ -45,23 +44,31 @@ struct _GbSearchContextClass
{
GObjectClass parent;
- void (*results_added) (GbSearchContext *context,
- GbSearchProvider *provider,
- GList *results,
- gboolean finished);
+ void (*result_added) (GbSearchContext *context,
+ GbSearchProvider *provider,
+ GbSearchResult *result);
+ void (*result_removed) (GbSearchContext *context,
+ GbSearchProvider *provider,
+ GbSearchResult *result);
};
-GType gb_search_context_get_type (void);
-GbSearchContext *gb_search_context_new (const GList *providers,
- const gchar *search_text);
-void gb_search_context_cancel (GbSearchContext *context);
-const GList *gb_search_context_get_results (GbSearchContext *context);
-void gb_search_context_add_results (GbSearchContext *context,
- GbSearchProvider *provider,
- GList *results,
- gboolean finished);
-void gb_search_context_execute (GbSearchContext *context);
-const gchar *gb_search_context_get_search_text (GbSearchContext *context);
+GbSearchContext *gb_search_context_new (void);
+const GList *gb_search_context_get_providers (GbSearchContext *context);
+void gb_search_context_add_provider (GbSearchContext *context,
+ GbSearchProvider *provider,
+ gsize max_results);
+void gb_search_context_add_result (GbSearchContext *context,
+ GbSearchProvider *provider,
+ GbSearchResult *result);
+void gb_search_context_remove_result (GbSearchContext *context,
+ GbSearchProvider *provider,
+ GbSearchResult *result);
+void gb_search_context_cancel (GbSearchContext *context);
+void gb_search_context_execute (GbSearchContext *context,
+ const gchar *search_terms);
+void gb_search_context_set_provider_count (GbSearchContext *context,
+ GbSearchProvider *provider,
+ guint64 count);
G_END_DECLS
diff --git a/src/search/gb-search-display-group.c b/src/search/gb-search-display-group.c
new file mode 100644
index 0000000..611e787
--- /dev/null
+++ b/src/search/gb-search-display-group.c
@@ -0,0 +1,457 @@
+/* gb-search-display-group.c
+ *
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <glib/gi18n.h>
+
+#include "gb-search-display-group.h"
+#include "gb-search-provider.h"
+#include "gb-search-result.h"
+#include "gb-widget.h"
+
+struct _GbSearchDisplayGroupPrivate
+{
+ /* References owned by instance */
+ GbSearchProvider *provider;
+
+ /* References owned by template */
+ GtkLabel *more_label;
+ GtkListBoxRow *more_row;
+ GtkLabel *label;
+ GtkListBox *rows;
+
+ guint64 count;
+};
+
+G_DEFINE_TYPE_WITH_PRIVATE (GbSearchDisplayGroup,
+ gb_search_display_group,
+ GTK_TYPE_BOX)
+
+enum {
+ PROP_0,
+ PROP_MAX_RESULTS,
+ PROP_PROVIDER,
+ PROP_SIZE_GROUP,
+ LAST_PROP
+};
+
+enum {
+ RESULT_SELECTED,
+ LAST_SIGNAL
+};
+
+static GQuark gQuarkResult;
+static GQuark gQuarkRow;
+static GParamSpec *gParamSpecs [LAST_PROP];
+static guint gSignals [LAST_SIGNAL];
+
+GbSearchProvider *
+gb_search_display_group_get_provider (GbSearchDisplayGroup *group)
+{
+ g_return_val_if_fail (GB_IS_SEARCH_DISPLAY_GROUP (group), NULL);
+
+ return group->priv->provider;
+}
+
+static void
+gb_search_display_group_set_provider (GbSearchDisplayGroup *group,
+ GbSearchProvider *provider)
+{
+ const gchar *verb;
+
+ g_return_if_fail (GB_IS_SEARCH_DISPLAY_GROUP (group));
+ g_return_if_fail (!provider || GB_IS_SEARCH_PROVIDER (provider));
+
+ if (provider)
+ {
+ group->priv->provider = g_object_ref (provider);
+ verb = gb_search_provider_get_verb (provider);
+ gtk_label_set_label (group->priv->label, verb);
+ }
+}
+
+static void
+gb_search_display_group_set_size_group (GbSearchDisplayGroup *group,
+ GtkSizeGroup *size_group)
+{
+ g_return_if_fail (GB_IS_SEARCH_DISPLAY_GROUP (group));
+ g_return_if_fail (!size_group || GTK_IS_SIZE_GROUP (size_group));
+
+ if (size_group)
+ gtk_size_group_add_widget (size_group, GTK_WIDGET (group->priv->label));
+}
+
+GtkWidget *
+gb_search_display_group_create_row (GbSearchResult *result)
+{
+ GtkProgressBar *progress;
+ GtkListBoxRow *row;
+ const gchar *markup;
+ GtkLabel *label;
+ GtkBox *box;
+ gfloat score;
+
+ g_return_val_if_fail (GB_IS_SEARCH_RESULT (result), NULL);
+
+ row = g_object_new (GTK_TYPE_LIST_BOX_ROW,
+ "visible", TRUE,
+ NULL);
+ g_object_set_qdata_full (G_OBJECT (row), gQuarkResult,
+ g_object_ref (result), g_object_unref);
+ g_object_set_qdata (G_OBJECT (result), gQuarkRow, row);
+ box = g_object_new (GTK_TYPE_BOX,
+ "orientation", GTK_ORIENTATION_HORIZONTAL,
+ "spacing", 6,
+ "visible", TRUE,
+ NULL);
+ gtk_container_add (GTK_CONTAINER (row), GTK_WIDGET (box));
+ markup = gb_search_result_get_markup (result);
+ label = g_object_new (GTK_TYPE_LABEL,
+ "hexpand", TRUE,
+ "label", markup,
+ "use-markup", TRUE,
+ "visible", TRUE,
+ "xalign", 0.0f,
+ "xpad", 6,
+ "ypad", 3,
+ NULL);
+ gtk_container_add (GTK_CONTAINER (box), GTK_WIDGET (label));
+ score = gb_search_result_get_score (result);
+ progress = g_object_new (GTK_TYPE_PROGRESS_BAR,
+ "fraction", score,
+ "hexpand", FALSE,
+ "inverted", TRUE,
+ "visible", TRUE,
+ "width-request", 30,
+ "valign", GTK_ALIGN_CENTER,
+ "margin-start", 6,
+ "margin-end", 6,
+ NULL);
+ gtk_style_context_add_class (
+ gtk_widget_get_style_context (GTK_WIDGET (progress)), "osd");
+ gtk_container_add (GTK_CONTAINER (box), GTK_WIDGET (progress));
+
+ return GTK_WIDGET (row);
+}
+
+void
+gb_search_display_group_remove_result (GbSearchDisplayGroup *group,
+ GbSearchResult *result)
+{
+ GtkWidget *row;
+
+ g_return_if_fail (GB_IS_SEARCH_DISPLAY_GROUP (group));
+ g_return_if_fail (GB_IS_SEARCH_RESULT (result));
+
+ row = g_object_get_qdata (G_OBJECT (result), gQuarkRow);
+
+ if (row)
+ gtk_container_remove (GTK_CONTAINER (group->priv->rows), row);
+}
+
+void
+gb_search_display_group_add_result (GbSearchDisplayGroup *group,
+ GbSearchResult *result)
+{
+ GtkWidget *row;
+
+ g_return_if_fail (GB_IS_SEARCH_DISPLAY_GROUP (group));
+ g_return_if_fail (GB_IS_SEARCH_RESULT (result));
+
+ row = gb_search_display_group_create_row (result);
+ gtk_container_add (GTK_CONTAINER (group->priv->rows), row);
+
+ gtk_list_box_invalidate_sort (group->priv->rows);
+
+ group->priv->count++;
+}
+
+void
+gb_search_display_group_set_count (GbSearchDisplayGroup *group,
+ guint64 count)
+{
+ GtkWidget *parent;
+ gchar *markup;
+
+ g_return_if_fail (GB_IS_SEARCH_DISPLAY_GROUP (group));
+
+ markup = g_strdup_printf (_("%"G_GUINT64_FORMAT" more"), count);
+ gtk_label_set_label (group->priv->more_label, markup);
+ g_free (markup);
+
+ parent = GTK_WIDGET (group->priv->more_row);
+
+ if ((count - group->priv->count) > 0)
+ gtk_widget_show (parent);
+ else
+ gtk_widget_hide (parent);
+}
+
+static gint
+compare_cb (GtkListBoxRow *row1,
+ GtkListBoxRow *row2,
+ gpointer user_data)
+{
+ GtkListBoxRow *more_row = user_data;
+ GbSearchResult *result1;
+ GbSearchResult *result2;
+ gfloat score1;
+ gfloat score2;
+
+ if (row1 == more_row)
+ return 1;
+ else if (row2 == more_row)
+ return -1;
+
+ result1 = g_object_get_qdata (G_OBJECT (row1), gQuarkResult);
+ result2 = g_object_get_qdata (G_OBJECT (row2), gQuarkResult);
+
+ score1 = gb_search_result_get_score (result1);
+ score2 = gb_search_result_get_score (result2);
+
+ if (score1 < score2)
+ return 1;
+ else if (score1 > score2)
+ return -1;
+ else
+ return 0;
+}
+
+void
+gb_search_display_group_unselect (GbSearchDisplayGroup *group)
+{
+ g_return_if_fail (GB_IS_SEARCH_DISPLAY_GROUP (group));
+
+ gtk_list_box_unselect_all (group->priv->rows);
+}
+
+static void
+gb_search_display_group_row_selected (GbSearchDisplayGroup *group,
+ GtkListBoxRow *row,
+ GtkListBox *list_box)
+{
+ g_return_if_fail (GB_IS_SEARCH_DISPLAY_GROUP (group));
+ g_return_if_fail (!row || GTK_IS_LIST_BOX_ROW (row));
+ g_return_if_fail (GTK_IS_LIST_BOX (list_box));
+
+ if (row)
+ {
+ GbSearchResult *result;
+
+ result = g_object_get_qdata (G_OBJECT (row), gQuarkResult);
+ g_signal_emit (group, gSignals [RESULT_SELECTED], 0, result);
+ }
+}
+
+void
+gb_search_display_group_focus_first (GbSearchDisplayGroup *group)
+{
+ GtkListBoxRow *row;
+
+ g_return_if_fail (GB_IS_SEARCH_DISPLAY_GROUP (group));
+
+ row = gtk_list_box_get_row_at_y (group->priv->rows, 1);
+
+ if (row)
+ {
+ gtk_list_box_unselect_all (group->priv->rows);
+ gtk_widget_child_focus (GTK_WIDGET (group->priv->rows), GTK_DIR_DOWN);
+ }
+}
+
+void
+gb_search_display_group_focus_last (GbSearchDisplayGroup *group)
+{
+ GtkAllocation alloc;
+ GtkListBoxRow *row;
+
+ g_return_if_fail (GB_IS_SEARCH_DISPLAY_GROUP (group));
+
+ gtk_widget_get_allocation (GTK_WIDGET (group->priv->rows), &alloc);
+ row = gtk_list_box_get_row_at_y (group->priv->rows, alloc.height - 2);
+
+ if (row)
+ {
+ gtk_list_box_unselect_all (group->priv->rows);
+ gtk_widget_child_focus (GTK_WIDGET (group->priv->rows), GTK_DIR_UP);
+ }
+}
+
+static void
+gb_search_display_group_header_cb (GtkListBoxRow *row,
+ GtkListBoxRow *before,
+ gpointer user_data)
+{
+ g_return_if_fail (GTK_IS_LIST_BOX_ROW (row));
+
+ if (row)
+ {
+ GtkWidget *header;
+
+ header = g_object_new (GTK_TYPE_SEPARATOR,
+ "orientation", GTK_ORIENTATION_HORIZONTAL,
+ "visible", TRUE,
+ NULL);
+ gtk_list_box_row_set_header (row, header);
+ }
+}
+
+static gboolean
+gb_search_display_group_keynav_failed (GbSearchDisplayGroup *group,
+ GtkDirectionType dir,
+ GtkListBox *list_box)
+{
+ gboolean ret = FALSE;
+
+ g_return_val_if_fail (GB_IS_SEARCH_DISPLAY_GROUP (group), FALSE);
+ g_return_val_if_fail (GTK_IS_LIST_BOX (list_box), FALSE);
+
+ g_signal_emit_by_name (group, "keynav-failed", dir, &ret);
+
+ return ret;
+}
+
+static void
+gb_search_display_group_finalize (GObject *object)
+{
+ GbSearchDisplayGroupPrivate *priv = GB_SEARCH_DISPLAY_GROUP (object)->priv;
+
+ g_clear_object (&priv->provider);
+
+ G_OBJECT_CLASS (gb_search_display_group_parent_class)->finalize (object);
+}
+
+static void
+gb_search_display_group_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GbSearchDisplayGroup *self = GB_SEARCH_DISPLAY_GROUP (object);
+
+ switch (prop_id)
+ {
+ case PROP_PROVIDER:
+ g_value_set_object (value, gb_search_display_group_get_provider (self));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+gb_search_display_group_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GbSearchDisplayGroup *self = GB_SEARCH_DISPLAY_GROUP (object);
+
+ switch (prop_id)
+ {
+ case PROP_PROVIDER:
+ gb_search_display_group_set_provider (self, g_value_get_object (value));
+ break;
+
+ case PROP_SIZE_GROUP:
+ gb_search_display_group_set_size_group (self, g_value_get_object (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+gb_search_display_group_class_init (GbSearchDisplayGroupClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+ object_class->finalize = gb_search_display_group_finalize;
+ object_class->get_property = gb_search_display_group_get_property;
+ object_class->set_property = gb_search_display_group_set_property;
+
+ gParamSpecs [PROP_PROVIDER] =
+ g_param_spec_object ("provider",
+ _("Provider"),
+ _("The search provider"),
+ GB_TYPE_SEARCH_PROVIDER,
+ (G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (object_class, PROP_PROVIDER,
+ gParamSpecs [PROP_PROVIDER]);
+
+ gParamSpecs [PROP_SIZE_GROUP] =
+ g_param_spec_object ("size-group",
+ _("Size Group"),
+ _("The size group for the label."),
+ GTK_TYPE_SIZE_GROUP,
+ (G_PARAM_WRITABLE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (object_class, PROP_SIZE_GROUP,
+ gParamSpecs [PROP_SIZE_GROUP]);
+
+ gSignals [RESULT_SELECTED] =
+ g_signal_new ("result-selected",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ 0,
+ NULL,
+ NULL,
+ g_cclosure_marshal_generic,
+ G_TYPE_NONE,
+ 1,
+ GB_TYPE_SEARCH_RESULT);
+
+ GB_WIDGET_CLASS_TEMPLATE (widget_class, "gb-search-display-group.ui");
+ GB_WIDGET_CLASS_BIND (widget_class, GbSearchDisplayGroup, more_label);
+ GB_WIDGET_CLASS_BIND (widget_class, GbSearchDisplayGroup, more_row);
+ GB_WIDGET_CLASS_BIND (widget_class, GbSearchDisplayGroup, label);
+ GB_WIDGET_CLASS_BIND (widget_class, GbSearchDisplayGroup, rows);
+
+ gQuarkResult = g_quark_from_static_string ("GB_SEARCH_RESULT");
+ gQuarkRow = g_quark_from_static_string ("GB_SEARCH_DISPLAY_ROW");
+}
+
+static void
+gb_search_display_group_init (GbSearchDisplayGroup *self)
+{
+ self->priv = gb_search_display_group_get_instance_private (self);
+
+ gtk_widget_init_template (GTK_WIDGET (self));
+
+ gtk_list_box_set_sort_func (self->priv->rows, compare_cb,
+ self->priv->more_row, NULL);
+
+ g_signal_connect_object (self->priv->rows,
+ "keynav-failed",
+ G_CALLBACK (gb_search_display_group_keynav_failed),
+ self,
+ G_CONNECT_SWAPPED);
+ g_signal_connect_object (self->priv->rows,
+ "row-selected",
+ G_CALLBACK (gb_search_display_group_row_selected),
+ self,
+ G_CONNECT_SWAPPED);
+ gtk_list_box_set_header_func (self->priv->rows,
+ gb_search_display_group_header_cb,
+ NULL, NULL);
+}
diff --git a/src/search/gb-search-display-group.h b/src/search/gb-search-display-group.h
new file mode 100644
index 0000000..ec17842
--- /dev/null
+++ b/src/search/gb-search-display-group.h
@@ -0,0 +1,62 @@
+/* gb-search-display-group.h
+ *
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GB_SEARCH_DISPLAY_GROUP_H
+#define GB_SEARCH_DISPLAY_GROUP_H
+
+#include <gtk/gtk.h>
+
+#include "gb-search-types.h"
+
+G_BEGIN_DECLS
+
+#define GB_SEARCH_DISPLAY_GROUP(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj),
GB_TYPE_SEARCH_DISPLAY_GROUP, GbSearchDisplayGroup))
+#define GB_SEARCH_DISPLAY_GROUP_CONST(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj),
GB_TYPE_SEARCH_DISPLAY_GROUP, GbSearchDisplayGroup const))
+#define GB_SEARCH_DISPLAY_GROUP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass),
GB_TYPE_SEARCH_DISPLAY_GROUP, GbSearchDisplayGroupClass))
+#define GB_IS_SEARCH_DISPLAY_GROUP(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj),
GB_TYPE_SEARCH_DISPLAY_GROUP))
+#define GB_IS_SEARCH_DISPLAY_GROUP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),
GB_TYPE_SEARCH_DISPLAY_GROUP))
+#define GB_SEARCH_DISPLAY_GROUP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj),
GB_TYPE_SEARCH_DISPLAY_GROUP, GbSearchDisplayGroupClass))
+
+struct _GbSearchDisplayGroup
+{
+ GtkBox parent;
+
+ /*< private >*/
+ GbSearchDisplayGroupPrivate *priv;
+};
+
+struct _GbSearchDisplayGroupClass
+{
+ GtkBoxClass parent;
+};
+
+void gb_search_display_group_clear (GbSearchDisplayGroup *group);
+GbSearchProvider *gb_search_display_group_get_provider (GbSearchDisplayGroup *group);
+void gb_search_display_group_add_result (GbSearchDisplayGroup *group,
+ GbSearchResult *result);
+void gb_search_display_group_remove_result (GbSearchDisplayGroup *group,
+ GbSearchResult *result);
+void gb_search_display_group_set_count (GbSearchDisplayGroup *group,
+ gsize count);
+void gb_search_display_group_unselect (GbSearchDisplayGroup *group);
+void gb_search_display_group_focus_first (GbSearchDisplayGroup *group);
+void gb_search_display_group_focus_last (GbSearchDisplayGroup *group);
+
+G_END_DECLS
+
+#endif /* GB_SEARCH_DISPLAY_GROUP_H */
diff --git a/src/search/gb-search-display.c b/src/search/gb-search-display.c
index af16d71..0ce09cc 100644
--- a/src/search/gb-search-display.c
+++ b/src/search/gb-search-display.c
@@ -1,6 +1,6 @@
/* gb-search-display.c
*
- * Copyright (C) 2014 Christian Hergert <christian hergert me>
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
*
* 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
@@ -16,28 +16,28 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#define G_LOG_DOMAIN "search-display"
-
#include <glib/gi18n.h>
-#include "gb-log.h"
-#include "gb-scrolled-window.h"
#include "gb-search-display.h"
+#include "gb-search-display-group.h"
#include "gb-search-provider.h"
#include "gb-search-result.h"
-#include "gb-widget.h"
struct _GbSearchDisplayPrivate
{
- /* References owned by widget */
- GbSearchContext *context;
-
- /* References owned by Gtk template */
- GtkListBox *list_box;
- GbScrolledWindow *scroller;
+ GbSearchContext *context;
+ GArray *providers;
+ GtkSizeGroup *size_group;
+ GbSearchDisplayGroup *last_group;
};
-G_DEFINE_TYPE_WITH_PRIVATE (GbSearchDisplay, gb_search_display, GTK_TYPE_BIN)
+typedef struct
+{
+ GbSearchProvider *provider;
+ GbSearchDisplayGroup *group;
+} ProviderEntry;
+
+G_DEFINE_TYPE_WITH_PRIVATE (GbSearchDisplay, gb_search_display, GTK_TYPE_BOX)
enum {
PROP_0,
@@ -53,6 +53,29 @@ enum {
static GParamSpec *gParamSpecs [LAST_PROP];
static guint gSignals [LAST_SIGNAL];
+static void
+provider_entry_destroy (gpointer data)
+{
+ ProviderEntry *entry = data;
+
+ g_clear_object (&entry->provider);
+}
+
+static gint
+provider_entry_sort (gconstpointer ptra,
+ gconstpointer ptrb)
+{
+ const ProviderEntry *entrya = ptra;
+ const ProviderEntry *entryb = ptrb;
+ gint a;
+ gint b;
+
+ a = gb_search_provider_get_priority ((GB_SEARCH_PROVIDER (entrya->provider)));
+ b = gb_search_provider_get_priority ((GB_SEARCH_PROVIDER (entryb->provider)));
+
+ return a - b;
+}
+
GtkWidget *
gb_search_display_new (void)
{
@@ -60,263 +83,353 @@ gb_search_display_new (void)
}
static void
-gb_search_display_results_added (GbSearchDisplay *display,
- GbSearchProvider *provider,
- GList *results,
- gboolean finished)
+gb_search_display_result_selected (GbSearchDisplay *display,
+ GbSearchResult *result,
+ GbSearchDisplayGroup *group)
{
- GList *iter;
-
- ENTRY;
+ guint i;
g_return_if_fail (GB_IS_SEARCH_DISPLAY (display));
- g_return_if_fail (GB_IS_SEARCH_PROVIDER (provider));
-
- for (iter = results; iter; iter = iter->next)
- gtk_list_box_insert (display->priv->list_box, iter->data, -1);
+ g_return_if_fail (!result || GB_IS_SEARCH_RESULT (result));
+ g_return_if_fail (GB_IS_SEARCH_DISPLAY_GROUP (group));
- gtk_list_box_invalidate_sort (display->priv->list_box);
+ for (i = 0; i < display->priv->providers->len; i++)
+ {
+ ProviderEntry *ptr;
- EXIT;
+ ptr = &g_array_index (display->priv->providers, ProviderEntry, i);
+ if (ptr->group != group)
+ gb_search_display_group_unselect (ptr->group);
+ }
}
-GbSearchContext *
-gb_search_display_get_context (GbSearchDisplay *display)
+static gboolean
+gb_search_display_keynav_failed (GbSearchDisplay *display,
+ GtkDirectionType dir,
+ GbSearchDisplayGroup *group)
{
- g_return_val_if_fail (GB_IS_SEARCH_DISPLAY (display), NULL);
+ GList *list;
+ GList *iter;
+ gint position = -1;
- return display->priv->context;
+ g_return_val_if_fail (GB_IS_SEARCH_DISPLAY (display), FALSE);
+ g_return_val_if_fail (GB_IS_SEARCH_DISPLAY_GROUP (group), FALSE);
+
+ gtk_container_child_get (GTK_CONTAINER (display), GTK_WIDGET (group),
+ "position", &position,
+ NULL);
+
+ if (dir == GTK_DIR_DOWN)
+ {
+ list = gtk_container_get_children (GTK_CONTAINER (display));
+ iter = g_list_nth (list, position + 1);
+ if (iter && (iter->data != display->priv->last_group))
+ {
+ gb_search_display_group_unselect (group);
+ gb_search_display_group_focus_first (iter->data);
+ return TRUE;
+ }
+ }
+ else if (dir == GTK_DIR_UP && position > 0)
+ {
+ list = gtk_container_get_children (GTK_CONTAINER (display));
+ iter = g_list_nth (list, position - 1);
+ if (iter)
+ {
+ gb_search_display_group_unselect (group);
+ gb_search_display_group_focus_last (iter->data);
+ return TRUE;
+ }
+ }
+
+ return FALSE;
}
-static void
-gb_search_display_connect (GbSearchDisplay *display,
- GbSearchContext *context)
+void
+gb_search_display_activate (GbSearchDisplay *display)
{
- GbSearchDisplayPrivate *priv;
- const GList *list;
- const GList *iter;
+ g_warning ("TODO: implement display_activate()");
+}
- ENTRY;
+static void
+gb_search_display_add_provider (GbSearchDisplay *display,
+ GbSearchProvider *provider)
+{
+ ProviderEntry entry = { 0 };
+ guint i;
g_return_if_fail (GB_IS_SEARCH_DISPLAY (display));
- g_return_if_fail (GB_IS_SEARCH_CONTEXT (context));
+ g_return_if_fail (GB_IS_SEARCH_PROVIDER (provider));
- priv = display->priv;
+ /*
+ * Make sure we don't add an item twice. Probably can assert here, but
+ * warning will do for now.
+ */
+ for (i = 0; i < display->priv->providers->len; i++)
+ {
+ ProviderEntry *ptr;
- g_signal_connect_object (context,
- "results-added",
- G_CALLBACK (gb_search_display_results_added),
+ ptr = &g_array_index (display->priv->providers, ProviderEntry, i);
+
+ if (ptr->provider == provider)
+ {
+ g_warning (_("Cannot add provider more than once."));
+ return;
+ }
+ }
+
+ /*
+ * Add the entry to our array and sort the array to determine our target
+ * widget packing position.
+ */
+ entry.provider = g_object_ref (provider);
+ entry.group = g_object_new (GB_TYPE_SEARCH_DISPLAY_GROUP,
+ "size-group", display->priv->size_group,
+ "provider", provider,
+ "visible", FALSE,
+ NULL);
+ g_signal_connect_object (entry.group,
+ "result-selected",
+ G_CALLBACK (gb_search_display_result_selected),
+ display,
+ G_CONNECT_SWAPPED);
+ g_signal_connect_object (entry.group,
+ "keynav-failed",
+ G_CALLBACK (gb_search_display_keynav_failed),
display,
G_CONNECT_SWAPPED);
+ g_array_append_val (display->priv->providers, entry);
+ g_array_sort (display->priv->providers, provider_entry_sort);
- list = gb_search_context_get_results (context);
- for (iter = list; iter; iter = iter->next)
- gtk_list_box_insert (priv->list_box, iter->data, -1);
- gtk_list_box_invalidate_sort (display->priv->list_box);
+ /*
+ * Find the location of the entry and use the index to pack the display
+ * group widget.
+ */
+ for (i = 0; i < display->priv->providers->len; i++)
+ {
+ ProviderEntry *ptr;
- EXIT;
+ ptr = &g_array_index (display->priv->providers, ProviderEntry, i);
+
+ if (ptr->provider == provider)
+ {
+ gtk_container_add_with_properties (GTK_CONTAINER (display),
+ GTK_WIDGET (entry.group),
+ "position", i,
+ NULL);
+ break;
+ }
+ }
}
static void
-gb_search_display_disconnect (GbSearchDisplay *display,
- GbSearchContext *context)
+gb_search_display_remove_provider (GbSearchDisplay *display,
+ GbSearchProvider *provider)
{
- GbSearchDisplayPrivate *priv;
- GList *children;
- GList *iter;
-
- ENTRY;
+ guint i;
g_return_if_fail (GB_IS_SEARCH_DISPLAY (display));
- g_return_if_fail (GB_IS_SEARCH_CONTEXT (context));
+ g_return_if_fail (GB_IS_SEARCH_PROVIDER (provider));
- priv = display->priv;
+ for (i = 0; i < display->priv->providers->len; i++)
+ {
+ ProviderEntry *ptr;
- g_signal_handlers_disconnect_by_func (context,
- G_CALLBACK (gb_search_display_results_added),
- display);
+ ptr = &g_array_index (display->priv->providers, ProviderEntry, i);
- children = gtk_container_get_children (GTK_CONTAINER (priv->list_box));
- for (iter = children; iter; iter = iter->next)
- gtk_container_remove (GTK_CONTAINER (priv->list_box), iter->data);
- g_list_free (children);
+ if (ptr->provider == provider)
+ {
+ gtk_container_remove (GTK_CONTAINER (display),
+ GTK_WIDGET (ptr->group));
+ g_array_remove_index (display->priv->providers, i);
+ return;
+ }
+ }
- EXIT;
+ g_warning (_("The provider could not be found."));
}
-void
-gb_search_display_set_context (GbSearchDisplay *display,
- GbSearchContext *context)
+static void
+gb_search_display_result_added (GbSearchDisplay *display,
+ GbSearchProvider *provider,
+ GbSearchResult *result,
+ GbSearchContext *context)
{
- ENTRY;
+ guint i;
g_return_if_fail (GB_IS_SEARCH_DISPLAY (display));
- g_return_if_fail (!context || GB_IS_SEARCH_CONTEXT (context));
+ g_return_if_fail (GB_IS_SEARCH_PROVIDER (provider));
+ g_return_if_fail (GB_IS_SEARCH_RESULT (result));
+ g_return_if_fail (GB_IS_SEARCH_CONTEXT (context));
- if (display->priv->context != context)
+ for (i = 0; i < display->priv->providers->len; i++)
{
- if (display->priv->context)
- {
- gb_search_display_disconnect (display, display->priv->context);
- g_clear_object (&display->priv->context);
- }
+ ProviderEntry *ptr;
- if (context)
+ ptr = &g_array_index (display->priv->providers, ProviderEntry, i);
+
+ if (ptr->provider == provider)
{
- display->priv->context = g_object_ref (context);
- gb_search_display_connect (display, context);
+ gb_search_display_group_add_result (ptr->group, result);
+ gtk_widget_show (GTK_WIDGET (ptr->group));
+ break;
}
-
- g_object_notify_by_pspec (G_OBJECT (display),
- gParamSpecs [PROP_CONTEXT]);
}
-
- EXIT;
}
static void
-gb_search_display_emit_result_activated (GbSearchDisplay *display,
- GbSearchResult *result)
+gb_search_display_result_removed (GbSearchDisplay *display,
+ GbSearchProvider *provider,
+ GbSearchResult *result,
+ GbSearchContext *context)
{
+ guint i;
+
g_return_if_fail (GB_IS_SEARCH_DISPLAY (display));
+ g_return_if_fail (GB_IS_SEARCH_PROVIDER (provider));
g_return_if_fail (GB_IS_SEARCH_RESULT (result));
+ g_return_if_fail (GB_IS_SEARCH_CONTEXT (context));
- gb_search_result_activate (result);
- g_signal_emit (display, gSignals [RESULT_ACTIVATED], 0, result);
+ for (i = 0; i < display->priv->providers->len; i++)
+ {
+ ProviderEntry *ptr;
+
+ ptr = &g_array_index (display->priv->providers, ProviderEntry, i);
+
+ if (ptr->provider == provider)
+ {
+ gb_search_display_group_remove_result (ptr->group, result);
+ break;
+ }
+ }
}
static void
-gb_search_display_row_activated (GbSearchDisplay *display,
- GtkListBoxRow *row,
- GtkListBox *list_box)
+gb_search_display_count_set (GbSearchDisplay *display,
+ GbSearchProvider *provider,
+ guint64 count,
+ GbSearchContext *context)
{
- GtkWidget *child;
+ guint i;
g_return_if_fail (GB_IS_SEARCH_DISPLAY (display));
- g_return_if_fail (GTK_IS_LIST_BOX_ROW (row));
- g_return_if_fail (GTK_IS_LIST_BOX (list_box));
+ g_return_if_fail (GB_IS_SEARCH_PROVIDER (provider));
+ g_return_if_fail (GB_IS_SEARCH_CONTEXT (context));
- child = gtk_bin_get_child (GTK_BIN (row));
+ for (i = 0; i < display->priv->providers->len; i++)
+ {
+ ProviderEntry *ptr;
- if (GB_IS_SEARCH_RESULT (child))
- gb_search_display_emit_result_activated (display, GB_SEARCH_RESULT (child));
+ ptr = &g_array_index (display->priv->providers, ProviderEntry, i);
+
+ if (ptr->provider == provider)
+ {
+ gb_search_display_group_set_count (ptr->group, count);
+ break;
+ }
+ }
}
-void
-gb_search_display_activate (GbSearchDisplay *display)
+static void
+gb_search_display_connect_context (GbSearchDisplay *display,
+ GbSearchContext *context)
{
- GtkListBoxRow *row;
+ const GList *providers;
+ const GList *iter;
g_return_if_fail (GB_IS_SEARCH_DISPLAY (display));
+ g_return_if_fail (GB_IS_SEARCH_CONTEXT (context));
- row = gtk_list_box_get_selected_row (display->priv->list_box);
-
- /*
- * WORKAROUND:
- *
- * Workaround since get_index() does not take into account sorts and
- * a y of 0 doesn't currently work.
- */
- if (!row)
- row = gtk_list_box_get_row_at_y (display->priv->list_box, 5);
-
- if (row)
- gb_search_display_row_activated (display, row, display->priv->list_box);
-}
-
-static gint
-gb_search_display_sort_cb (GtkListBoxRow *row1,
- GtkListBoxRow *row2,
- gpointer user_data)
-{
- GtkWidget *child1;
- GtkWidget *child2;
+ providers = gb_search_context_get_providers (context);
- child1 = gtk_bin_get_child (GTK_BIN (row1));
- child2 = gtk_bin_get_child (GTK_BIN (row2));
+ for (iter = providers; iter; iter = iter->next)
+ gb_search_display_add_provider (display, iter->data);
- return gb_search_result_compare_func (GB_SEARCH_RESULT (child1),
- GB_SEARCH_RESULT (child2));
+ g_signal_connect_object (context,
+ "result-added",
+ G_CALLBACK (gb_search_display_result_added),
+ display,
+ G_CONNECT_SWAPPED);
+ g_signal_connect_object (context,
+ "result-removed",
+ G_CALLBACK (gb_search_display_result_removed),
+ display,
+ G_CONNECT_SWAPPED);
+ g_signal_connect_object (context,
+ "count-set",
+ G_CALLBACK (gb_search_display_count_set),
+ display,
+ G_CONNECT_SWAPPED);
}
static void
-gb_search_display_grab_focus (GtkWidget *widget)
+gb_search_display_disconnect_context (GbSearchDisplay *display,
+ GbSearchContext *context)
{
- GbSearchDisplay *display = (GbSearchDisplay *)widget;
- GtkListBoxRow *row;
-
g_return_if_fail (GB_IS_SEARCH_DISPLAY (display));
+ g_return_if_fail (GB_IS_SEARCH_CONTEXT (context));
- /*
- * WORKAROUND:
- *
- * Getting the row at y of 0 does not work (returns NULL). And getting the
- * row at index 0 does not take into account sort.
- *
- * https://bugzilla.gnome.org/show_bug.cgi?id=741208
- */
- row = gtk_list_box_get_row_at_y (display->priv->list_box, 1);
-
- if (row)
+ while (display->priv->providers->len)
{
- gtk_list_box_select_row (display->priv->list_box, row);
- gtk_widget_child_focus (GTK_WIDGET (display->priv->list_box),
- GTK_DIR_TAB_FORWARD);
+ ProviderEntry *ptr;
+
+ ptr = &g_array_index (display->priv->providers, ProviderEntry,
+ display->priv->providers->len - 1);
+ gb_search_display_remove_provider (display, ptr->provider);
}
- else
- GTK_WIDGET_CLASS (gb_search_display_parent_class)->grab_focus (widget);
+
+ g_signal_handlers_disconnect_by_func (context,
+ G_CALLBACK (gb_search_display_result_added),
+ display);
}
-static void
-gb_search_display_header_func (GtkListBoxRow *row,
- GtkListBoxRow *before,
- gpointer user_data)
+GbSearchContext *
+gb_search_display_get_context (GbSearchDisplay *display)
{
- if (before)
- {
- GtkWidget *header;
+ g_return_val_if_fail (GB_IS_SEARCH_DISPLAY (display), NULL);
- header = g_object_new (GTK_TYPE_SEPARATOR,
- "orientation", GTK_ORIENTATION_HORIZONTAL,
- "visible", TRUE,
- NULL);
- gtk_list_box_row_set_header (row, header);
- }
+ return display->priv->context;
}
-static void
-gb_search_display_constructed (GObject *object)
+void
+gb_search_display_set_context (GbSearchDisplay *display,
+ GbSearchContext *context)
{
- GbSearchDisplay *self = (GbSearchDisplay *)object;
-
- g_return_if_fail (GB_IS_SEARCH_DISPLAY (self));
+ GbSearchDisplayPrivate *priv;
- G_OBJECT_CLASS (gb_search_display_parent_class)->constructed (object);
+ g_return_if_fail (GB_IS_SEARCH_DISPLAY (display));
+ g_return_if_fail (!context || GB_IS_SEARCH_CONTEXT (context));
- gtk_list_box_set_header_func (self->priv->list_box,
- gb_search_display_header_func,
- NULL, NULL);
+ priv = display->priv;
+ if (priv->context != context)
+ {
+ if (priv->context)
+ {
+ gb_search_display_disconnect_context (display, priv->context);
+ g_clear_object (&display->priv->context);
+ }
- g_signal_connect_object (self->priv->list_box,
- "row-activated",
- G_CALLBACK (gb_search_display_row_activated),
- self,
- G_CONNECT_SWAPPED);
+ if (context)
+ {
+ priv->context = g_object_ref (context);
+ gb_search_display_connect_context (display, priv->context);
+ }
- gtk_list_box_set_sort_func (self->priv->list_box,
- gb_search_display_sort_cb,
- NULL, NULL);
+ g_object_notify_by_pspec (G_OBJECT (display), gParamSpecs [PROP_CONTEXT]);
+ }
}
static void
-gb_search_display_finalize (GObject *object)
+gb_search_display_dispose (GObject *object)
{
GbSearchDisplayPrivate *priv = GB_SEARCH_DISPLAY (object)->priv;
+ g_clear_pointer (&priv->providers, g_array_unref);
g_clear_object (&priv->context);
+ g_clear_object (&priv->size_group);
- G_OBJECT_CLASS (gb_search_display_parent_class)->finalize (object);
+ G_OBJECT_CLASS (gb_search_display_parent_class)->dispose (object);
}
static void
@@ -330,7 +443,7 @@ gb_search_display_get_property (GObject *object,
switch (prop_id)
{
case PROP_CONTEXT:
- g_value_set_object (value, self->priv->context);
+ g_value_set_object (value, gb_search_display_get_context (self));
break;
default:
@@ -361,19 +474,15 @@ static void
gb_search_display_class_init (GbSearchDisplayClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
- GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
- object_class->constructed = gb_search_display_constructed;
- object_class->finalize = gb_search_display_finalize;
+ object_class->dispose = gb_search_display_dispose;
object_class->get_property = gb_search_display_get_property;
object_class->set_property = gb_search_display_set_property;
- widget_class->grab_focus = gb_search_display_grab_focus;
-
gParamSpecs [PROP_CONTEXT] =
g_param_spec_object ("context",
_("Context"),
- _("The search context."),
+ _("The active search context."),
GB_TYPE_SEARCH_CONTEXT,
(G_PARAM_READWRITE |
G_PARAM_STATIC_STRINGS));
@@ -391,12 +500,6 @@ gb_search_display_class_init (GbSearchDisplayClass *klass)
G_TYPE_NONE,
1,
GB_TYPE_SEARCH_RESULT);
-
- GB_WIDGET_CLASS_TEMPLATE (klass, "gb-search-display.ui");
- GB_WIDGET_CLASS_BIND (klass, GbSearchDisplay, list_box);
- GB_WIDGET_CLASS_BIND (klass, GbSearchDisplay, scroller);
-
- g_type_ensure (GB_TYPE_SCROLLED_WINDOW);
}
static void
@@ -404,5 +507,20 @@ gb_search_display_init (GbSearchDisplay *self)
{
self->priv = gb_search_display_get_instance_private (self);
- gtk_widget_init_template (GTK_WIDGET (self));
+ self->priv->providers = g_array_new (FALSE, FALSE, sizeof (ProviderEntry));
+ g_array_set_clear_func (self->priv->providers,
+ (GDestroyNotify)provider_entry_destroy);
+
+ self->priv->size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
+
+ gtk_orientable_set_orientation (GTK_ORIENTABLE (self),
+ GTK_ORIENTATION_VERTICAL);
+
+ self->priv->last_group = g_object_new (GB_TYPE_SEARCH_DISPLAY_GROUP,
+ "size-group", self->priv->size_group,
+ "visible", TRUE,
+ "vexpand", TRUE,
+ NULL);
+ gtk_container_add (GTK_CONTAINER (self),
+ GTK_WIDGET (self->priv->last_group));
}
diff --git a/src/search/gb-search-display.h b/src/search/gb-search-display.h
index bd4e39d..7958c21 100644
--- a/src/search/gb-search-display.h
+++ b/src/search/gb-search-display.h
@@ -1,6 +1,6 @@
/* gb-search-display.h
*
- * Copyright (C) 2014 Christian Hergert <christian hergert me>
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
*
* 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
@@ -22,12 +22,9 @@
#include <gtk/gtk.h>
#include "gb-search-types.h"
-#include "gb-search-context.h"
-#include "gb-search-result.h"
G_BEGIN_DECLS
-#define GB_TYPE_SEARCH_DISPLAY (gb_search_display_get_type())
#define GB_SEARCH_DISPLAY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GB_TYPE_SEARCH_DISPLAY,
GbSearchDisplay))
#define GB_SEARCH_DISPLAY_CONST(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GB_TYPE_SEARCH_DISPLAY,
GbSearchDisplay const))
#define GB_SEARCH_DISPLAY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GB_TYPE_SEARCH_DISPLAY,
GbSearchDisplayClass))
@@ -35,13 +32,9 @@ G_BEGIN_DECLS
#define GB_IS_SEARCH_DISPLAY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GB_TYPE_SEARCH_DISPLAY))
#define GB_SEARCH_DISPLAY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GB_TYPE_SEARCH_DISPLAY,
GbSearchDisplayClass))
-typedef struct _GbSearchDisplay GbSearchDisplay;
-typedef struct _GbSearchDisplayClass GbSearchDisplayClass;
-typedef struct _GbSearchDisplayPrivate GbSearchDisplayPrivate;
-
struct _GbSearchDisplay
{
- GtkBin parent;
+ GtkBox parent;
/*< private >*/
GbSearchDisplayPrivate *priv;
@@ -49,18 +42,17 @@ struct _GbSearchDisplay
struct _GbSearchDisplayClass
{
- GtkBinClass parent;
+ GtkBoxClass parent;
void (*result_activated) (GbSearchDisplay *display,
GbSearchResult *result);
};
-GType gb_search_display_get_type (void);
GtkWidget *gb_search_display_new (void);
+void gb_search_display_activate (GbSearchDisplay *display);
GbSearchContext *gb_search_display_get_context (GbSearchDisplay *display);
void gb_search_display_set_context (GbSearchDisplay *display,
GbSearchContext *context);
-void gb_search_display_activate (GbSearchDisplay *display);
G_END_DECLS
diff --git a/src/search/gb-search-manager.c b/src/search/gb-search-manager.c
index a7c3862..afbf6aa 100644
--- a/src/search/gb-search-manager.c
+++ b/src/search/gb-search-manager.c
@@ -1,6 +1,6 @@
/* gb-search-manager.c
*
- * Copyright (C) 2014 Christian Hergert <christian hergert me>
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
*
* 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
@@ -16,13 +16,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#define G_LOG_DOMAIN "search-manager"
-
-#include <glib/gi18n.h>
-
-#include "gb-search-context.h"
#include "gb-search-manager.h"
-#include "gb-search-provider.h"
struct _GbSearchManagerPrivate
{
@@ -32,11 +26,11 @@ struct _GbSearchManagerPrivate
G_DEFINE_TYPE_WITH_PRIVATE (GbSearchManager, gb_search_manager, G_TYPE_OBJECT)
enum {
- PROP_0,
- LAST_PROP
+ PROVIDER_ADDED,
+ LAST_SIGNAL
};
-//static GParamSpec *gParamSpecs [LAST_PROP];
+static guint gSignals [LAST_SIGNAL];
GbSearchManager *
gb_search_manager_new (void)
@@ -44,28 +38,45 @@ gb_search_manager_new (void)
return g_object_new (GB_TYPE_SEARCH_MANAGER, NULL);
}
-GbSearchManager *
-gb_search_manager_get_default (void)
+GbSearchContext *
+gb_search_manager_search (GbSearchManager *manager,
+ const GList *providers,
+ const gchar *search_terms)
{
- static GbSearchManager *instance;
+ GbSearchContext *context;
+ const GList *iter;
+
+ g_return_val_if_fail (GB_IS_SEARCH_MANAGER (manager), NULL);
+ g_return_val_if_fail (search_terms, NULL);
+
+ if (!providers)
+ providers = manager->priv->providers;
+
+ if (!providers)
+ return NULL;
- if (!instance)
- instance = gb_search_manager_new ();
+ context = gb_search_context_new ();
- return instance;
+ for (iter = providers; iter; iter = iter->next)
+ gb_search_context_add_provider (context, iter->data, 0);
+
+ return context;
}
-static gint
-sort_provider (gconstpointer a,
- gconstpointer b)
+/**
+ * gb_search_manager_get_providers:
+ *
+ * Returns the providers attached to the search manager.
+ *
+ * Returns: (transfer container) (element-type GbSearchProvider*): A #GList of
+ * #GbSearchProvider.
+ */
+GList *
+gb_search_manager_get_providers (GbSearchManager *manager)
{
- gint prio1;
- gint prio2;
-
- prio1 = gb_search_provider_get_priority ((GbSearchProvider *)a);
- prio2 = gb_search_provider_get_priority ((GbSearchProvider *)b);
+ g_return_val_if_fail (GB_IS_SEARCH_MANAGER (manager), NULL);
- return prio1 - prio2;
+ return g_list_copy (manager->priv->providers);
}
void
@@ -75,26 +86,9 @@ gb_search_manager_add_provider (GbSearchManager *manager,
g_return_if_fail (GB_IS_SEARCH_MANAGER (manager));
g_return_if_fail (GB_IS_SEARCH_PROVIDER (provider));
- manager->priv->providers =
- g_list_sort (g_list_prepend (manager->priv->providers,
- g_object_ref (provider)),
- sort_provider);
-}
-
-GbSearchContext *
-gb_search_manager_search (GbSearchManager *manager,
- const gchar *search_text)
-{
- GbSearchContext *context;
-
- g_return_val_if_fail (GB_IS_SEARCH_MANAGER (manager), NULL);
- g_return_val_if_fail (search_text, NULL);
-
- context = gb_search_context_new (manager->priv->providers, search_text);
-
- gb_search_context_execute (context);
-
- return context;
+ manager->priv->providers = g_list_append (manager->priv->providers,
+ g_object_ref (provider));
+ g_signal_emit (manager, gSignals [PROVIDER_ADDED], 0, provider);
}
static void
@@ -103,49 +97,30 @@ gb_search_manager_finalize (GObject *object)
GbSearchManagerPrivate *priv = GB_SEARCH_MANAGER (object)->priv;
g_list_foreach (priv->providers, (GFunc)g_object_unref, NULL);
- g_clear_pointer (&priv->providers, g_list_free);
+ g_list_free (priv->providers);
+ priv->providers = NULL;
G_OBJECT_CLASS (gb_search_manager_parent_class)->finalize (object);
}
static void
-gb_search_manager_get_property (GObject *object,
- guint prop_id,
- GValue *value,
- GParamSpec *pspec)
-{
- //GbSearchManager *self = GB_SEARCH_MANAGER (object);
-
- switch (prop_id)
- {
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
- }
-}
-
-static void
-gb_search_manager_set_property (GObject *object,
- guint prop_id,
- const GValue *value,
- GParamSpec *pspec)
-{
- //GbSearchManager *self = GB_SEARCH_MANAGER (object);
-
- switch (prop_id)
- {
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
- }
-}
-
-static void
gb_search_manager_class_init (GbSearchManagerClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->finalize = gb_search_manager_finalize;
- object_class->get_property = gb_search_manager_get_property;
- object_class->set_property = gb_search_manager_set_property;
+
+ gSignals [PROVIDER_ADDED] =
+ g_signal_new ("provider-added",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ 0,
+ NULL,
+ NULL,
+ g_cclosure_marshal_generic,
+ G_TYPE_NONE,
+ 1,
+ GB_TYPE_SEARCH_PROVIDER);
}
static void
diff --git a/src/search/gb-search-manager.h b/src/search/gb-search-manager.h
index 5c862fe..4683448 100644
--- a/src/search/gb-search-manager.h
+++ b/src/search/gb-search-manager.h
@@ -1,6 +1,6 @@
/* gb-search-manager.h
*
- * Copyright (C) 2014 Christian Hergert <christian hergert me>
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
*
* 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
@@ -19,13 +19,13 @@
#ifndef GB_SEARCH_MANAGER_H
#define GB_SEARCH_MANAGER_H
-#include <gio/gio.h>
+#include <glib-object.h>
-#include "gb-search-types.h"
+#include "gb-search-context.h"
+#include "gb-search-provider.h"
G_BEGIN_DECLS
-#define GB_TYPE_SEARCH_MANAGER (gb_search_manager_get_type())
#define GB_SEARCH_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GB_TYPE_SEARCH_MANAGER,
GbSearchManager))
#define GB_SEARCH_MANAGER_CONST(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GB_TYPE_SEARCH_MANAGER,
GbSearchManager const))
#define GB_SEARCH_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GB_TYPE_SEARCH_MANAGER,
GbSearchManagerClass))
@@ -33,10 +33,6 @@ G_BEGIN_DECLS
#define GB_IS_SEARCH_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GB_TYPE_SEARCH_MANAGER))
#define GB_SEARCH_MANAGER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GB_TYPE_SEARCH_MANAGER,
GbSearchManagerClass))
-typedef struct _GbSearchManager GbSearchManager;
-typedef struct _GbSearchManagerClass GbSearchManagerClass;
-typedef struct _GbSearchManagerPrivate GbSearchManagerPrivate;
-
struct _GbSearchManager
{
GObject parent;
@@ -50,13 +46,13 @@ struct _GbSearchManagerClass
GObjectClass parent;
};
-GType gb_search_manager_get_type (void);
-GbSearchManager *gb_search_manager_new (void);
-GbSearchManager *gb_search_manager_get_default (void);
-void gb_search_manager_add_provider (GbSearchManager *manager,
- GbSearchProvider *provider);
-GbSearchContext *gb_search_manager_search (GbSearchManager *manager,
- const gchar *search_text);
+GbSearchManager *gb_search_manager_new (void);
+GList *gb_search_manager_get_providers (GbSearchManager *manager);
+void gb_search_manager_add_provider (GbSearchManager *manager,
+ GbSearchProvider *provider);
+GbSearchContext *gb_search_manager_search (GbSearchManager *manager,
+ const GList *providers,
+ const gchar *search_terms);
G_END_DECLS
diff --git a/src/search/gb-search-provider.c b/src/search/gb-search-provider.c
index d43cab8..345ddff 100644
--- a/src/search/gb-search-provider.c
+++ b/src/search/gb-search-provider.c
@@ -1,6 +1,6 @@
/* gb-search-provider.c
*
- * Copyright (C) 2014 Christian Hergert <christian hergert me>
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
*
* 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
@@ -16,54 +16,121 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#include "gb-search-context.h"
+#include <glib/gi18n.h>
+
#include "gb-search-provider.h"
-G_DEFINE_INTERFACE (GbSearchProvider, gb_search_provider, G_TYPE_OBJECT)
+G_DEFINE_ABSTRACT_TYPE (GbSearchProvider, gb_search_provider, G_TYPE_OBJECT)
-static void
-gb_search_provider_default_init (GbSearchProviderInterface *iface)
+const gchar *
+gb_search_provider_get_verb (GbSearchProvider *provider)
{
+ g_return_val_if_fail (GB_IS_SEARCH_PROVIDER (provider), NULL);
+
+ if (GB_SEARCH_PROVIDER_GET_CLASS (provider)->get_verb)
+ return GB_SEARCH_PROVIDER_GET_CLASS (provider)->get_verb (provider);
+
+ return NULL;
}
-/**
- * gb_search_provider_get_priority:
- * @provider: A #GbSearchProvider
- *
- * Retrieves the priority of the search provider. Lower integral values are
- * of higher priority.
- *
- * Returns: An integer.
- */
gint
gb_search_provider_get_priority (GbSearchProvider *provider)
{
- g_return_val_if_fail (GB_IS_SEARCH_PROVIDER (provider), 0);
+ g_return_val_if_fail (GB_IS_SEARCH_PROVIDER (provider), NULL);
+
+ if (GB_SEARCH_PROVIDER_GET_CLASS (provider)->get_priority)
+ return GB_SEARCH_PROVIDER_GET_CLASS (provider)->get_priority (provider);
- if (GB_SEARCH_PROVIDER_GET_INTERFACE (provider)->get_priority)
- return GB_SEARCH_PROVIDER_GET_INTERFACE (provider)->get_priority (provider);
- return 0;
+ return G_MAXINT;
+}
+
+gunichar
+gb_search_provider_get_prefix (GbSearchProvider *provider)
+{
+ g_return_val_if_fail (GB_IS_SEARCH_PROVIDER (provider), NULL);
+
+ if (GB_SEARCH_PROVIDER_GET_CLASS (provider)->get_prefix)
+ return GB_SEARCH_PROVIDER_GET_CLASS (provider)->get_prefix (provider);
+
+ return '\0';
}
-/**
- * gb_search_provider_populate:
- * @provider: A #GbSearchProvider
- * @context: A #GbSearchContext
- * @cancelalble: An optional #GCancellable to cancel the request.
- *
- * Requests that a search provider start populating @context with results.
- * If @cancellable is not %NULL, then it may be used to cancel the request.
- */
void
gb_search_provider_populate (GbSearchProvider *provider,
GbSearchContext *context,
+ const gchar *search_terms,
+ gsize max_results,
GCancellable *cancellable)
{
g_return_if_fail (GB_IS_SEARCH_PROVIDER (provider));
g_return_if_fail (GB_IS_SEARCH_CONTEXT (context));
+ g_return_if_fail (search_terms);
g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
- if (GB_SEARCH_PROVIDER_GET_INTERFACE (provider)->populate)
- GB_SEARCH_PROVIDER_GET_INTERFACE (provider)->populate (provider, context,
- cancellable);
+ if (GB_SEARCH_PROVIDER_GET_CLASS (provider)->populate)
+ {
+ GB_SEARCH_PROVIDER_GET_CLASS (provider)->populate (provider,
+ context,
+ search_terms,
+ max_results,
+ cancellable);
+ return;
+ }
+
+ g_warning ("%s does not implement populate vfunc",
+ g_type_name (G_TYPE_FROM_INSTANCE (provider)));
+}
+
+static void
+gb_search_provider_finalize (GObject *object)
+{
+ GbSearchProviderPrivate *priv = GB_SEARCH_PROVIDER (object)->priv;
+
+ G_OBJECT_CLASS (gb_search_provider_parent_class)->finalize (object);
+}
+
+static void
+gb_search_provider_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GbSearchProvider *self = GB_SEARCH_PROVIDER (object);
+
+ switch (prop_id)
+ {
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+gb_search_provider_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GbSearchProvider *self = GB_SEARCH_PROVIDER (object);
+
+ switch (prop_id)
+ {
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+gb_search_provider_class_init (GbSearchProviderClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = gb_search_provider_finalize;
+ object_class->get_property = gb_search_provider_get_property;
+ object_class->set_property = gb_search_provider_set_property;
+}
+
+static void
+gb_search_provider_init (GbSearchProvider *self)
+{
+ self->priv = gb_search_provider_get_instance_private (self);
}
diff --git a/src/search/gb-search-provider.h b/src/search/gb-search-provider.h
index f209d52..78300e3 100644
--- a/src/search/gb-search-provider.h
+++ b/src/search/gb-search-provider.h
@@ -1,6 +1,6 @@
/* gb-search-provider.h
*
- * Copyright (C) 2014 Christian Hergert <christian hergert me>
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
*
* 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
@@ -21,30 +21,47 @@
#include <gio/gio.h>
-#include "gb-search-types.h"
+#include "gb-search-context.h"
G_BEGIN_DECLS
-#define GB_TYPE_SEARCH_PROVIDER (gb_search_provider_get_type ())
-#define GB_SEARCH_PROVIDER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj),
GB_TYPE_SEARCH_PROVIDER, GbSearchProvider))
-#define GB_IS_SEARCH_PROVIDER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj),
GB_TYPE_SEARCH_PROVIDER))
-#define GB_SEARCH_PROVIDER_GET_INTERFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj),
GB_TYPE_SEARCH_PROVIDER, GbSearchProviderInterface))
+#define GB_SEARCH_PROVIDER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GB_TYPE_SEARCH_PROVIDER,
GbSearchProvider))
+#define GB_SEARCH_PROVIDER_CONST(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GB_TYPE_SEARCH_PROVIDER,
GbSearchProvider const))
+#define GB_SEARCH_PROVIDER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GB_TYPE_SEARCH_PROVIDER,
GbSearchProviderClass))
+#define GB_IS_SEARCH_PROVIDER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GB_TYPE_SEARCH_PROVIDER))
+#define GB_IS_SEARCH_PROVIDER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GB_TYPE_SEARCH_PROVIDER))
+#define GB_SEARCH_PROVIDER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GB_TYPE_SEARCH_PROVIDER,
GbSearchProviderClass))
-struct _GbSearchProviderInterface
+struct _GbSearchProvider
{
- GTypeInterface parent;
+ GObject parent;
- gint (*get_priority) (GbSearchProvider *provider);
- void (*populate) (GbSearchProvider *provider,
- GbSearchContext *context,
- GCancellable *cancellable);
+ /*< private >*/
+ GbSearchProviderPrivate *priv;
};
-GType gb_search_provider_get_type (void);
-gint gb_search_provider_get_priority (GbSearchProvider *provider);
-void gb_search_provider_populate (GbSearchProvider *provider,
- GbSearchContext *context,
- GCancellable *cancellable);
+struct _GbSearchProviderClass
+{
+ GObjectClass parent;
+
+ gunichar (*get_prefix) (GbSearchProvider *provider);
+ gint (*get_priority) (GbSearchProvider *provider);
+ const gchar *(*get_verb) (GbSearchProvider *provider);
+ void (*populate) (GbSearchProvider *provider,
+ GbSearchContext *context,
+ const gchar *search_terms,
+ gsize max_results,
+ GCancellable *cancellable);
+};
+
+gunichar gb_search_provider_get_prefix (GbSearchProvider *provider);
+gint gb_search_provider_get_priority (GbSearchProvider *provider);
+const gchar *gb_search_provider_get_verb (GbSearchProvider *provider);
+void gb_search_provider_populate (GbSearchProvider *provider,
+ GbSearchContext *context,
+ const gchar *search_terms,
+ gsize max_results,
+ GCancellable *cancellable);
G_END_DECLS
diff --git a/src/search/gb-search-reducer.c b/src/search/gb-search-reducer.c
new file mode 100644
index 0000000..a20b84e
--- /dev/null
+++ b/src/search/gb-search-reducer.c
@@ -0,0 +1,98 @@
+/* gb-search-reducer.c
+ *
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "gb-search-context.h"
+#include "gb-search-provider.h"
+#include "gb-search-reducer.h"
+#include "gb-search-result.h"
+
+void
+gb_search_reducer_init (GbSearchReducer *reducer,
+ GbSearchContext *context,
+ GbSearchProvider *provider)
+{
+ g_return_if_fail (reducer);
+ g_return_if_fail (GB_IS_SEARCH_CONTEXT (context));
+ g_return_if_fail (GB_IS_SEARCH_PROVIDER (provider));
+
+ reducer->context = context;
+ reducer->provider = provider;
+ reducer->sequence = g_sequence_new (g_object_unref);
+ reducer->max_results = 15;
+ reducer->count = 0;
+}
+
+void
+gb_search_reducer_destroy (GbSearchReducer *reducer)
+{
+ g_return_if_fail (reducer);
+
+ g_sequence_free (reducer->sequence);
+}
+
+void
+gb_search_reducer_push (GbSearchReducer *reducer,
+ GbSearchResult *result)
+{
+ g_return_if_fail (reducer);
+ g_return_if_fail (GB_IS_SEARCH_RESULT (result));
+
+ if (reducer->max_results <= g_sequence_get_length (reducer->sequence))
+ {
+ GSequenceIter *iter;
+ GbSearchResult *lowest;
+
+ /* Remove lowest score */
+ iter = g_sequence_get_begin_iter (reducer->sequence);
+ lowest = g_sequence_get (iter);
+ gb_search_context_remove_result (reducer->context, reducer->provider,
+ lowest);
+ g_sequence_remove (iter);
+ }
+
+ g_sequence_insert_sorted (reducer->sequence,
+ g_object_ref (result),
+ (GCompareDataFunc)gb_search_result_compare,
+ NULL);
+ gb_search_context_add_result (reducer->context, reducer->provider, result);
+}
+
+gboolean
+gb_search_reducer_accepts (GbSearchReducer *reducer,
+ gfloat score)
+{
+ GSequenceIter *iter;
+
+ g_return_val_if_fail (reducer, FALSE);
+
+ if (g_sequence_get_length (reducer->sequence) < reducer->max_results)
+ return TRUE;
+
+ iter = g_sequence_get_begin_iter (reducer->sequence);
+
+ if (iter)
+ {
+ GbSearchResult *result;
+
+ result = g_sequence_get (iter);
+ if (result)
+ return score > gb_search_result_get_score (result);
+ }
+
+ return FALSE;
+}
diff --git a/src/search/gb-search-reducer.h b/src/search/gb-search-reducer.h
new file mode 100644
index 0000000..461ee02
--- /dev/null
+++ b/src/search/gb-search-reducer.h
@@ -0,0 +1,49 @@
+/* gb-search-reducer.h
+ *
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GB_SEARCH_REDUCER_H
+#define GB_SEARCH_REDUCER_H
+
+#include <glib.h>
+
+#include "gb-search-types.h"
+
+G_BEGIN_DECLS
+
+typedef struct
+{
+ GbSearchContext *context;
+ GbSearchProvider *provider;
+ GSequence *sequence;
+ gsize max_results;
+ gsize count;
+} GbSearchReducer;
+
+void gb_search_reducer_init (GbSearchReducer *reducer,
+ GbSearchContext *context,
+ GbSearchProvider *provider);
+gboolean gb_search_reducer_accepts (GbSearchReducer *reducer,
+ gfloat score);
+void gb_search_reducer_push (GbSearchReducer *reducer,
+ GbSearchResult *result);
+void gb_search_reducer_destroy (GbSearchReducer *reducer);
+
+
+G_END_DECLS
+
+#endif /* GB_SEARCH_REDUCER_H */
diff --git a/src/search/gb-search-result.c b/src/search/gb-search-result.c
index 3de3c56..da9fe60 100644
--- a/src/search/gb-search-result.c
+++ b/src/search/gb-search-result.c
@@ -1,6 +1,6 @@
/* gb-search-result.c
*
- * Copyright (C) 2014 Christian Hergert <christian hergert me>
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
*
* 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
@@ -22,86 +22,90 @@
struct _GbSearchResultPrivate
{
- gint priority;
- gfloat score;
+ gchar *markup;
+ gfloat score;
};
-G_DEFINE_TYPE_WITH_PRIVATE (GbSearchResult, gb_search_result, GTK_TYPE_BIN)
+G_DEFINE_TYPE_WITH_PRIVATE (GbSearchResult, gb_search_result, G_TYPE_OBJECT)
enum {
PROP_0,
+ PROP_MARKUP,
PROP_SCORE,
LAST_PROP
};
-enum {
- ACTIVATE,
- LAST_SIGNAL
-};
-
static GParamSpec *gParamSpecs [LAST_PROP];
-static guint gSignals [LAST_SIGNAL];
-GtkWidget *
-gb_search_result_new (void)
+gint
+gb_search_result_compare (const GbSearchResult *a,
+ const GbSearchResult *b)
{
- return g_object_new (GB_TYPE_SEARCH_RESULT, NULL);
+ if (a->priv->score < b->priv->score)
+ return -1;
+ else if (a->priv->score > b->priv->score)
+ return 1;
+ else
+ return 0;
}
-gfloat
-gb_search_result_get_score (GbSearchResult *result)
+GbSearchResult *
+gb_search_result_new (const gchar *markup,
+ gfloat score)
{
- g_return_val_if_fail (GB_IS_SEARCH_RESULT (result), 0.0f);
+ return g_object_new (GB_TYPE_SEARCH_RESULT,
+ "markup", markup,
+ "score", score,
+ NULL);
+}
- return result->priv->score;
+const gchar *
+gb_search_result_get_markup (GbSearchResult *result)
+{
+ g_return_val_if_fail (GB_IS_SEARCH_RESULT (result), NULL);
+
+ return result->priv->markup;
}
-void
-gb_search_result_set_score (GbSearchResult *result,
- gfloat score)
+static void
+gb_search_result_set_markup (GbSearchResult *result,
+ const gchar *markup)
{
g_return_if_fail (GB_IS_SEARCH_RESULT (result));
- if (result->priv->score != score)
+ if (result->priv->markup != markup)
{
- result->priv->score = score;
- g_object_notify_by_pspec (G_OBJECT (result),
- gParamSpecs [PROP_SCORE]);
+ g_free (result->priv->markup);
+ result->priv->markup = g_strdup (markup);
}
}
-gint
-gb_search_result_compare_func (gconstpointer result1,
- gconstpointer result2)
+gfloat
+gb_search_result_get_score (GbSearchResult *result)
{
- const GbSearchResult *r1 = result1;
- const GbSearchResult *r2 = result2;
- gint ret;
-
- ret = r2->priv->priority - r1->priv->priority;
-
- if (ret == 0)
- {
- if (r2->priv->score > r1->priv->score)
- return 1;
- else if (r2->priv->score < r1->priv->score)
- return -1;
- }
+ g_return_val_if_fail (GB_IS_SEARCH_RESULT (result), 0.0f);
- return ret;
+ return result->priv->score;
}
-void
-gb_search_result_activate (GbSearchResult *result)
+static void
+gb_search_result_set_score (GbSearchResult *result,
+ gfloat score)
{
g_return_if_fail (GB_IS_SEARCH_RESULT (result));
+ g_return_if_fail (score >= 0.0);
+ g_return_if_fail (score <= 1.0);
- g_signal_emit (result, gSignals [ACTIVATE], 0);
+ result->priv->score = score;
}
static void
gb_search_result_finalize (GObject *object)
{
+ GbSearchResultPrivate *priv = GB_SEARCH_RESULT (object)->priv;
+
+ g_clear_pointer (&priv->markup, g_free);
+
G_OBJECT_CLASS (gb_search_result_parent_class)->finalize (object);
}
@@ -115,6 +119,10 @@ gb_search_result_get_property (GObject *object,
switch (prop_id)
{
+ case PROP_MARKUP:
+ g_value_set_string (value, gb_search_result_get_markup (self));
+ break;
+
case PROP_SCORE:
g_value_set_float (value, gb_search_result_get_score (self));
break;
@@ -134,6 +142,10 @@ gb_search_result_set_property (GObject *object,
switch (prop_id)
{
+ case PROP_MARKUP:
+ gb_search_result_set_markup (self, g_value_get_string (value));
+ break;
+
case PROP_SCORE:
gb_search_result_set_score (self, g_value_get_float (value));
break;
@@ -152,27 +164,29 @@ gb_search_result_class_init (GbSearchResultClass *klass)
object_class->get_property = gb_search_result_get_property;
object_class->set_property = gb_search_result_set_property;
+ gParamSpecs [PROP_MARKUP] =
+ g_param_spec_string ("markup",
+ _("Markup"),
+ _("The pango markup to be rendered."),
+ NULL,
+ (G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (object_class, PROP_MARKUP,
+ gParamSpecs [PROP_MARKUP]);
+
gParamSpecs [PROP_SCORE] =
g_param_spec_float ("score",
_("Score"),
- _("The score for the result."),
+ _("The result match score."),
0.0f,
1.0f,
0.0f,
- (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+ (G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS));
g_object_class_install_property (object_class, PROP_SCORE,
gParamSpecs [PROP_SCORE]);
-
- gSignals [ACTIVATE] =
- g_signal_new ("activate",
- G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST,
- G_STRUCT_OFFSET (GbSearchResultClass, activate),
- NULL,
- NULL,
- g_cclosure_marshal_VOID__VOID,
- G_TYPE_NONE,
- 0);
}
static void
diff --git a/src/search/gb-search-result.h b/src/search/gb-search-result.h
index db24fec..63515a2 100644
--- a/src/search/gb-search-result.h
+++ b/src/search/gb-search-result.h
@@ -1,6 +1,6 @@
/* gb-search-result.h
*
- * Copyright (C) 2014 Christian Hergert <christian hergert me>
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
*
* 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
@@ -19,13 +19,12 @@
#ifndef GB_SEARCH_RESULT_H
#define GB_SEARCH_RESULT_H
-#include <gtk/gtk.h>
+#include <glib-object.h>
#include "gb-search-types.h"
G_BEGIN_DECLS
-#define GB_TYPE_SEARCH_RESULT (gb_search_result_get_type())
#define GB_SEARCH_RESULT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GB_TYPE_SEARCH_RESULT,
GbSearchResult))
#define GB_SEARCH_RESULT_CONST(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GB_TYPE_SEARCH_RESULT,
GbSearchResult const))
#define GB_SEARCH_RESULT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GB_TYPE_SEARCH_RESULT,
GbSearchResultClass))
@@ -35,7 +34,7 @@ G_BEGIN_DECLS
struct _GbSearchResult
{
- GtkBin parent;
+ GObject parent;
/*< private >*/
GbSearchResultPrivate *priv;
@@ -43,19 +42,15 @@ struct _GbSearchResult
struct _GbSearchResultClass
{
- GtkBinClass parent;
-
- void (*activate) (GbSearchResult *result);
+ GObjectClass parent;
};
-void gb_search_result_activate (GbSearchResult *result);
-gint gb_search_result_compare_func (gconstpointer result1,
- gconstpointer result2);
-GType gb_search_result_get_type (void);
-GtkWidget *gb_search_result_new (void);
-gfloat gb_search_result_get_score (GbSearchResult *result);
-void gb_search_result_set_score (GbSearchResult *result,
- gfloat score);
+GbSearchResult *gb_search_result_new (const gchar *markup,
+ gfloat score);
+gfloat gb_search_result_get_score (GbSearchResult *result);
+const gchar *gb_search_result_get_markup (GbSearchResult *result);
+gint gb_search_result_compare (const GbSearchResult *a,
+ const GbSearchResult *b);
G_END_DECLS
diff --git a/src/search/gb-search-types.h b/src/search/gb-search-types.h
index d6f8cd2..160c1c8 100644
--- a/src/search/gb-search-types.h
+++ b/src/search/gb-search-types.h
@@ -1,6 +1,6 @@
/* gb-search-types.h
*
- * Copyright (C) 2014 Christian Hergert <christian hergert me>
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
*
* 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
@@ -23,16 +23,43 @@
G_BEGIN_DECLS
-typedef struct _GbSearchContext GbSearchContext;
-typedef struct _GbSearchContextClass GbSearchContextClass;
-typedef struct _GbSearchContextPrivate GbSearchContextPrivate;
+#define GB_TYPE_SEARCH_CONTEXT (gb_search_context_get_type())
+#define GB_TYPE_SEARCH_DISPLAY (gb_search_display_get_type())
+#define GB_TYPE_SEARCH_DISPLAY_GROUP (gb_search_display_group_get_type())
+#define GB_TYPE_SEARCH_MANAGER (gb_search_manager_get_type())
+#define GB_TYPE_SEARCH_PROVIDER (gb_search_provider_get_type())
+#define GB_TYPE_SEARCH_RESULT (gb_search_result_get_type())
-typedef struct _GbSearchProvider GbSearchProvider;
-typedef struct _GbSearchProviderInterface GbSearchProviderInterface;
+typedef struct _GbSearchContext GbSearchContext;
+typedef struct _GbSearchContextClass GbSearchContextClass;
+typedef struct _GbSearchContextPrivate GbSearchContextPrivate;
-typedef struct _GbSearchResult GbSearchResult;
-typedef struct _GbSearchResultClass GbSearchResultClass;
-typedef struct _GbSearchResultPrivate GbSearchResultPrivate;
+typedef struct _GbSearchDisplay GbSearchDisplay;
+typedef struct _GbSearchDisplayClass GbSearchDisplayClass;
+typedef struct _GbSearchDisplayPrivate GbSearchDisplayPrivate;
+
+typedef struct _GbSearchDisplayGroup GbSearchDisplayGroup;
+typedef struct _GbSearchDisplayGroupClass GbSearchDisplayGroupClass;
+typedef struct _GbSearchDisplayGroupPrivate GbSearchDisplayGroupPrivate;
+
+typedef struct _GbSearchProvider GbSearchProvider;
+typedef struct _GbSearchProviderClass GbSearchProviderClass;
+typedef struct _GbSearchProviderPrivate GbSearchProviderPrivate;
+
+typedef struct _GbSearchManager GbSearchManager;
+typedef struct _GbSearchManagerClass GbSearchManagerClass;
+typedef struct _GbSearchManagerPrivate GbSearchManagerPrivate;
+
+typedef struct _GbSearchResult GbSearchResult;
+typedef struct _GbSearchResultClass GbSearchResultClass;
+typedef struct _GbSearchResultPrivate GbSearchResultPrivate;
+
+GType gb_search_context_get_type (void);
+GType gb_search_display_get_type (void);
+GType gb_search_display_group_get_type (void);
+GType gb_search_manager_get_type (void);
+GType gb_search_provider_get_type (void);
+GType gb_search_result_get_type (void);
G_END_DECLS
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]