[gnome-builder/wip/chergert/perspective] search: port search to libide
- From: Christian Hergert <chergert src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-builder/wip/chergert/perspective] search: port search to libide
- Date: Tue, 24 Nov 2015 09:35:54 +0000 (UTC)
commit 3b94edcebbe92ad307a25bcd2c85a251c79651a6
Author: Christian Hergert <chergert redhat com>
Date: Tue Nov 24 01:35:39 2015 -0800
search: port search to libide
This has a few minor style design changes too, but not everything we are
going to need. I'd also like to drop the arrow on the popover if possible.
It's handy to reuse popover for focus tracking and subsurfaces, but we
might consider just managing that ourself if necessary.
data/theme/Adwaita-dark.css | 5 +
data/theme/Adwaita.css | 5 +
data/theme/shared.css | 8 +
data/ui/gb-search-display-group.ui | 62 --
data/ui/gb-search-display-row.ui | 49 --
.../{gb-search-box.ui => ide-omni-search-entry.ui} | 10 +-
data/ui/ide-omni-search-group.ui | 14 +
data/ui/ide-omni-search-row.ui | 25 +
data/ui/ide-workbench-header-bar.ui | 3 +-
libide/Makefile.am | 8 +
libide/editor/ide-editor-perspective.c | 1 +
libide/ide.h | 1 +
libide/resources/libide.gresource.xml | 3 +
libide/search/ide-omni-search-display.c | 598 ++++++++++++++++++++
libide/search/ide-omni-search-display.h | 39 ++
libide/search/ide-omni-search-entry.c | 396 +++++++++++++
libide/search/ide-omni-search-entry.h | 38 ++
libide/search/ide-omni-search-group.c | 449 +++++++++++++++
libide/search/ide-omni-search-group.h | 45 ++
libide/search/ide-omni-search-row.c | 184 ++++++
libide/search/ide-omni-search-row.h | 38 ++
plugins/file-search/gb-file-search-index.c | 4 +-
plugins/file-search/gb-file-search-provider.c | 12 +-
23 files changed, 1870 insertions(+), 127 deletions(-)
---
diff --git a/data/theme/Adwaita-dark.css b/data/theme/Adwaita-dark.css
index 0a1bcb7..2d13ecf 100644
--- a/data/theme/Adwaita-dark.css
+++ b/data/theme/Adwaita-dark.css
@@ -84,3 +84,8 @@ workbench > GtkOverlay > stack > box > stackswitcher.vertical:first-child button
workbench > GtkOverlay > stack > box > stackswitcher.vertical:first-child button:checked {
color: #babdb6;
}
+
+
+omnisearchrow {
+ color: #eeeeec;
+}
diff --git a/data/theme/Adwaita.css b/data/theme/Adwaita.css
index a06d9dc..2e2b086 100644
--- a/data/theme/Adwaita.css
+++ b/data/theme/Adwaita.css
@@ -83,3 +83,8 @@ workbench > GtkOverlay > stack > box > stackswitcher.vertical:first-child button
workbench > GtkOverlay > stack > box > stackswitcher.vertical:first-child button:checked {
color: #2e3436;
}
+
+
+omnisearchrow {
+ color: #2e3436;
+}
diff --git a/data/theme/shared.css b/data/theme/shared.css
index 775250c..b2a951a 100644
--- a/data/theme/shared.css
+++ b/data/theme/shared.css
@@ -180,3 +180,11 @@ workbench IdePreferencesBin entry {
box-shadow: none;
opacity: 0.50;
}
+
+
+omnisearchrow {
+ padding-top: 9px;
+ padding-left: 12px;
+ padding-right: 12px;
+ padding-bottom: 9px;
+}
diff --git a/data/ui/gb-search-box.ui b/data/ui/ide-omni-search-entry.ui
similarity index 84%
rename from data/ui/gb-search-box.ui
rename to data/ui/ide-omni-search-entry.ui
index baab89c..09dce37 100644
--- a/data/ui/gb-search-box.ui
+++ b/data/ui/ide-omni-search-entry.ui
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<!-- interface-requires gtk+ 3.14 -->
- <template class="GbSearchBox" parent="GtkBox">
+ <template class="IdeOmniSearchEntry" parent="GtkBox">
<property name="orientation">horizontal</property>
<style>
<class name="linked"/>
@@ -16,7 +16,7 @@
<child>
<object class="GtkMenuButton" id="button">
<property name="focus-on-click">false</property>
- <property name="visible">true</property>
+ <property name="visible">false</property>
<property name="popover">popover</property>
<style>
<class name="button"/>
@@ -39,11 +39,11 @@
<child>
<object class="EggScrolledWindow" id="scroller">
<property name="max-content-height">800</property>
- <property name="min-content-height">50</property>
- <property name="min-content-width">775</property>
+ <property name="min-content-height">30</property>
+ <property name="min-content-width">550</property>
<property name="visible">True</property>
<child>
- <object class="GbSearchDisplay" id="display">
+ <object class="IdeOmniSearchDisplay" id="display">
<property name="visible">true</property>
</object>
</child>
diff --git a/data/ui/ide-omni-search-group.ui b/data/ui/ide-omni-search-group.ui
new file mode 100644
index 0000000..e4e0fb9
--- /dev/null
+++ b/data/ui/ide-omni-search-group.ui
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <!-- interface-requires gtk+ 3.8 -->
+ <template class="IdeOmniSearchGroup" parent="GtkBox">
+ <property name="orientation">vertical</property>
+ <property name="visible">true</property>
+ <child>
+ <object class="GtkListBox" id="rows">
+ <property name="hexpand">True</property>
+ <property name="visible">True</property>
+ </object>
+ </child>
+ </template>
+</interface>
diff --git a/data/ui/ide-omni-search-row.ui b/data/ui/ide-omni-search-row.ui
new file mode 100644
index 0000000..bfb167c
--- /dev/null
+++ b/data/ui/ide-omni-search-row.ui
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <!-- interface-requires gtk+ 3.18 -->
+ <template class="IdeOmniSearchRow" parent="GtkListBoxRow">
+ <child>
+ <object class="GtkBox">
+ <property name="visible">true</property>
+ <property name="orientation">horizontal</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkImage" id="image">
+ <property name="icon-name">folder-symbolic</property>
+ <property name="visible">true</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel" id="title">
+ <property name="visible">true</property>
+ <property name="xalign">0.0</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </template>
+</interface>
diff --git a/data/ui/ide-workbench-header-bar.ui b/data/ui/ide-workbench-header-bar.ui
index 39e92b6..9f1396c 100644
--- a/data/ui/ide-workbench-header-bar.ui
+++ b/data/ui/ide-workbench-header-bar.ui
@@ -4,11 +4,10 @@
<template class="IdeWorkbenchHeaderBar" parent="GtkHeaderBar">
<property name="show-close-button">true</property>
<child type="title">
- <object class="GtkSearchEntry" id="search_entry">
+ <object class="IdeOmniSearchEntry" id="search_entry">
<property name="hexpand">true</property>
<property name="margin-end">6</property>
<property name="margin-start">6</property>
- <property name="max-width-chars">50</property>
<property name="visible">true</property>
</object>
</child>
diff --git a/libide/Makefile.am b/libide/Makefile.am
index 0f073bc..53e4592 100644
--- a/libide/Makefile.am
+++ b/libide/Makefile.am
@@ -349,6 +349,14 @@ libide_1_0_la_SOURCES = \
preferences/ide-preferences-spin-button.h \
preferences/ide-preferences-switch.c \
preferences/ide-preferences-switch.h \
+ search/ide-omni-search-display.c \
+ search/ide-omni-search-display.h \
+ search/ide-omni-search-entry.c \
+ search/ide-omni-search-entry.h \
+ search/ide-omni-search-group.c \
+ search/ide-omni-search-group.h \
+ search/ide-omni-search-row.c \
+ search/ide-omni-search-row.h \
theatrics/ide-box-theatric.c \
theatrics/ide-box-theatric.h \
util/ide-cairo.c \
diff --git a/libide/editor/ide-editor-perspective.c b/libide/editor/ide-editor-perspective.c
index d413213..a4bc8dd 100644
--- a/libide/editor/ide-editor-perspective.c
+++ b/libide/editor/ide-editor-perspective.c
@@ -155,6 +155,7 @@ ide_editor_perspective_locate_buffer (GtkWidget *view,
if (stack != NULL)
{
ide_layout_stack_set_active_view (IDE_LAYOUT_STACK (stack), view);
+ gtk_widget_grab_focus (GTK_WIDGET (view));
*buffer = NULL;
}
}
diff --git a/libide/ide.h b/libide/ide.h
index f27f34f..f592fc6 100644
--- a/libide/ide.h
+++ b/libide/ide.h
@@ -121,6 +121,7 @@ G_BEGIN_DECLS
#include "git/ide-git-remote-callbacks.h"
#include "git/ide-git-vcs.h"
#include "local/ide-local-device.h"
+#include "search/ide-omni-search-row.h"
#include "util/ide-file-manager.h"
#include "util/ide-gtk.h"
#include "util/ide-line-reader.h"
diff --git a/libide/resources/libide.gresource.xml b/libide/resources/libide.gresource.xml
index a3c330f..201fd89 100644
--- a/libide/resources/libide.gresource.xml
+++ b/libide/resources/libide.gresource.xml
@@ -50,6 +50,9 @@
<file alias="ide-layout-pane.ui">../../data/ui/ide-layout-pane.ui</file>
<file alias="ide-layout-stack.ui">../../data/ui/ide-layout-stack.ui</file>
<file alias="ide-layout-view.ui">../../data/ui/ide-layout-view.ui</file>
+ <file alias="ide-omni-search-entry.ui">../../data/ui/ide-omni-search-entry.ui</file>
+ <file alias="ide-omni-search-group.ui">../../data/ui/ide-omni-search-group.ui</file>
+ <file alias="ide-omni-search-row.ui">../../data/ui/ide-omni-search-row.ui</file>
<file alias="ide-preferences-entry.ui">../../data/ui/ide-preferences-entry.ui</file>
<file alias="ide-preferences-font-button.ui">../../data/ui/ide-preferences-font-button.ui</file>
<file alias="ide-preferences-group.ui">../../data/ui/ide-preferences-group.ui</file>
diff --git a/libide/search/ide-omni-search-display.c b/libide/search/ide-omni-search-display.c
new file mode 100644
index 0000000..8f84436
--- /dev/null
+++ b/libide/search/ide-omni-search-display.c
@@ -0,0 +1,598 @@
+/* ide-omni-search-display.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/>.
+ */
+
+#define G_LOG_DOMAIN "ide-omni-search-display"
+
+#include <glib/gi18n.h>
+
+#include "ide-omni-search-group.h"
+#include "ide-omni-search-display.h"
+
+struct _IdeOmniSearchDisplay
+{
+ GtkBox parent_instance;
+
+ IdeSearchContext *context;
+ GPtrArray *providers;
+};
+
+typedef struct
+{
+ IdeSearchProvider *provider;
+ IdeOmniSearchGroup *group;
+} ProviderEntry;
+
+G_DEFINE_TYPE (IdeOmniSearchDisplay, ide_omni_search_display, GTK_TYPE_BOX)
+
+enum {
+ PROP_0,
+ PROP_CONTEXT,
+ LAST_PROP
+};
+
+enum {
+ RESULT_ACTIVATED,
+ LAST_SIGNAL
+};
+
+static GParamSpec *properties [LAST_PROP];
+static guint signals [LAST_SIGNAL];
+
+static void
+provider_entry_destroy (gpointer data)
+{
+ ProviderEntry *entry = data;
+
+ IDE_ENTRY;
+
+ IDE_TRACE_MSG ("releasing %p", data);
+
+ ide_clear_weak_pointer (&entry->group);
+ g_clear_object (&entry->provider);
+ g_free (entry);
+
+ IDE_EXIT;
+}
+
+static gint
+provider_entry_sort (gconstpointer ptra,
+ gconstpointer ptrb)
+{
+ ProviderEntry **entrya = (ProviderEntry **)ptra;
+ ProviderEntry **entryb = (ProviderEntry **)ptrb;
+ gint a;
+ gint b;
+
+ a = ide_search_provider_get_priority ((IDE_SEARCH_PROVIDER ((*entrya)->provider)));
+ b = ide_search_provider_get_priority ((IDE_SEARCH_PROVIDER ((*entryb)->provider)));
+
+ return a - b;
+}
+
+GtkWidget *
+ide_omni_search_display_new (void)
+{
+ return g_object_new (IDE_TYPE_OMNI_SEARCH_DISPLAY, NULL);
+}
+
+static void
+ide_omni_search_display_real_result_activated (IdeOmniSearchDisplay *self,
+ IdeSearchResult *result)
+{
+ g_return_if_fail (IDE_IS_OMNI_SEARCH_DISPLAY (self));
+ g_return_if_fail (IDE_IS_SEARCH_RESULT (result));
+
+}
+
+static void
+ide_omni_search_display_result_activated (IdeOmniSearchDisplay *self,
+ GtkWidget *widget,
+ IdeSearchResult *result,
+ IdeOmniSearchGroup *group)
+{
+ g_return_if_fail (IDE_IS_OMNI_SEARCH_DISPLAY (self));
+ g_return_if_fail (GTK_IS_WIDGET (widget));
+ g_return_if_fail (IDE_IS_SEARCH_RESULT (result));
+ g_return_if_fail (IDE_IS_OMNI_SEARCH_GROUP (group));
+
+ g_signal_emit (self, signals [RESULT_ACTIVATED], 0, result);
+}
+
+static void
+ide_omni_search_display_result_selected (IdeOmniSearchDisplay *self,
+ IdeSearchResult *result,
+ IdeOmniSearchGroup *group)
+{
+ guint i;
+
+ g_return_if_fail (IDE_IS_OMNI_SEARCH_DISPLAY (self));
+ g_return_if_fail (!result || IDE_IS_SEARCH_RESULT (result));
+ g_return_if_fail (IDE_IS_OMNI_SEARCH_GROUP (group));
+
+ for (i = 0; i < self->providers->len; i++)
+ {
+ ProviderEntry *ptr;
+
+ ptr = g_ptr_array_index (self->providers, i);
+ if ((ptr->group != NULL) && (ptr->group != group))
+ ide_omni_search_group_unselect (ptr->group);
+ }
+}
+
+static gboolean
+ide_omni_search_display_keynav_failed (IdeOmniSearchDisplay *self,
+ GtkDirectionType dir,
+ IdeOmniSearchGroup *group)
+{
+ GList *list = NULL;
+ GList *iter;
+ gint position = -1;
+ gboolean ret = FALSE;
+
+ g_return_val_if_fail (IDE_IS_OMNI_SEARCH_DISPLAY (self), FALSE);
+ g_return_val_if_fail (IDE_IS_OMNI_SEARCH_GROUP (group), FALSE);
+
+ gtk_container_child_get (GTK_CONTAINER (self), GTK_WIDGET (group),
+ "position", &position,
+ NULL);
+
+ if (dir == GTK_DIR_DOWN)
+ {
+ list = gtk_container_get_children (GTK_CONTAINER (self));
+ iter = g_list_nth (list, position + 1);
+ for (; iter; iter = iter->next)
+ {
+ if (ide_omni_search_group_get_first (iter->data))
+ {
+ ide_omni_search_group_unselect (group);
+ ide_omni_search_group_focus_first (iter->data);
+ ret = TRUE;
+ }
+ }
+ }
+ else if (dir == GTK_DIR_UP && position > 0)
+ {
+ list = gtk_container_get_children (GTK_CONTAINER (self));
+ iter = g_list_nth (list, position - 1);
+ for (; iter; iter = iter->prev)
+ {
+ if (ide_omni_search_group_get_first (iter->data))
+ {
+ ide_omni_search_group_unselect (group);
+ ide_omni_search_group_focus_last (iter->data);
+ ret = TRUE;
+ }
+ }
+ }
+
+ g_list_free (list);
+
+ return ret;
+}
+
+void
+ide_omni_search_display_activate (IdeOmniSearchDisplay *self)
+{
+ gsize i;
+
+ g_return_if_fail (IDE_IS_OMNI_SEARCH_DISPLAY (self));
+
+ for (i = 0; i < self->providers->len; i++)
+ {
+ ProviderEntry *ptr;
+
+ ptr = g_ptr_array_index (self->providers, i);
+
+ if (ptr->group != NULL)
+ {
+ if (ide_omni_search_group_activate (ptr->group))
+ break;
+ }
+ }
+}
+
+static void
+ide_omni_search_display_add_provider (IdeOmniSearchDisplay *self,
+ IdeSearchProvider *provider)
+{
+ ProviderEntry *entry;
+ guint i;
+
+ g_return_if_fail (IDE_IS_OMNI_SEARCH_DISPLAY (self));
+ g_return_if_fail (IDE_IS_SEARCH_PROVIDER (provider));
+
+ /*
+ * Make sure we don't add an item twice. Probably can assert here, but
+ * warning will do for now.
+ */
+ for (i = 0; i < self->providers->len; i++)
+ {
+ ProviderEntry *ptr;
+
+ ptr = g_ptr_array_index (self->providers, 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 = g_new0 (ProviderEntry, 1);
+ entry->provider = g_object_ref (provider);
+ entry->group = g_object_new (IDE_TYPE_OMNI_SEARCH_GROUP,
+ "provider", provider,
+ "visible", FALSE,
+ NULL);
+ g_object_add_weak_pointer (G_OBJECT (entry->group), (gpointer *)&entry->group);
+ g_signal_connect_object (entry->group,
+ "result-activated",
+ G_CALLBACK (ide_omni_search_display_result_activated),
+ self,
+ G_CONNECT_SWAPPED);
+ g_signal_connect_object (entry->group,
+ "result-selected",
+ G_CALLBACK (ide_omni_search_display_result_selected),
+ self,
+ G_CONNECT_SWAPPED);
+ g_signal_connect_object (entry->group,
+ "keynav-failed",
+ G_CALLBACK (ide_omni_search_display_keynav_failed),
+ self,
+ G_CONNECT_SWAPPED);
+ g_ptr_array_add (self->providers, entry);
+ g_ptr_array_sort (self->providers, provider_entry_sort);
+
+ /*
+ * Find the location of the entry and use the index to pack the display
+ * group widget.
+ */
+ for (i = 0; i < self->providers->len; i++)
+ {
+ ProviderEntry *ptr;
+
+ ptr = g_ptr_array_index (self->providers, i);
+
+ if (ptr->provider == provider)
+ {
+ gtk_container_add_with_properties (GTK_CONTAINER (self),
+ GTK_WIDGET (entry->group),
+ "position", i,
+ NULL);
+ break;
+ }
+ }
+}
+
+static void
+ide_omni_search_display_remove_provider (IdeOmniSearchDisplay *self,
+ IdeSearchProvider *provider)
+{
+ guint i;
+
+ g_return_if_fail (IDE_IS_OMNI_SEARCH_DISPLAY (self));
+ g_return_if_fail (IDE_IS_SEARCH_PROVIDER (provider));
+
+ for (i = 0; i < self->providers->len; i++)
+ {
+ ProviderEntry *ptr;
+
+ ptr = g_ptr_array_index (self->providers, i);
+
+ if (ptr->provider == provider)
+ {
+ IdeOmniSearchGroup *group = ptr->group;
+
+ if (group)
+ gtk_container_remove (GTK_CONTAINER (self), GTK_WIDGET (group));
+ g_ptr_array_remove_index (self->providers, i);
+ return;
+ }
+ }
+
+ g_warning (_("The provider could not be found."));
+}
+
+static void
+ide_omni_search_display_result_added (IdeOmniSearchDisplay *self,
+ IdeSearchProvider *provider,
+ IdeSearchResult *result,
+ IdeSearchContext *context)
+{
+ guint i;
+
+ g_return_if_fail (IDE_IS_OMNI_SEARCH_DISPLAY (self));
+ g_return_if_fail (IDE_IS_SEARCH_PROVIDER (provider));
+ g_return_if_fail (IDE_IS_SEARCH_RESULT (result));
+ g_return_if_fail (IDE_IS_SEARCH_CONTEXT (context));
+
+ for (i = 0; i < self->providers->len; i++)
+ {
+ ProviderEntry *ptr;
+
+ ptr = g_ptr_array_index (self->providers, i);
+
+ if (ptr->provider == provider)
+ {
+ if (ptr->group != NULL)
+ {
+ ide_omni_search_group_add_result (ptr->group, result);
+ gtk_widget_show (GTK_WIDGET (ptr->group));
+ }
+ break;
+ }
+ }
+}
+
+static void
+ide_omni_search_display_result_removed (IdeOmniSearchDisplay *self,
+ IdeSearchProvider *provider,
+ IdeSearchResult *result,
+ IdeSearchContext *context)
+{
+ guint i;
+
+ g_return_if_fail (IDE_IS_OMNI_SEARCH_DISPLAY (self));
+ g_return_if_fail (IDE_IS_SEARCH_PROVIDER (provider));
+ g_return_if_fail (IDE_IS_SEARCH_RESULT (result));
+ g_return_if_fail (IDE_IS_SEARCH_CONTEXT (context));
+
+ for (i = 0; i < self->providers->len; i++)
+ {
+ ProviderEntry *ptr;
+
+ ptr = g_ptr_array_index (self->providers, i);
+
+ if (ptr->provider == provider)
+ {
+ if (ptr->group != NULL)
+ ide_omni_search_group_remove_result (ptr->group, result);
+ break;
+ }
+ }
+}
+
+static void
+ide_omni_search_display_count_set (IdeOmniSearchDisplay *self,
+ IdeSearchProvider *provider,
+ guint64 count,
+ IdeSearchContext *context)
+{
+#if 0
+ guint i;
+
+ g_return_if_fail (IDE_IS_OMNI_SEARCH_DISPLAY (self));
+ g_return_if_fail (IDE_IS_SEARCH_PROVIDER (provider));
+ g_return_if_fail (IDE_IS_SEARCH_CONTEXT (context));
+
+ for (i = 0; i < self->providers->len; i++)
+ {
+ ProviderEntry *ptr;
+
+ ptr = g_ptr_array_index (self->providers, i);
+
+ if (ptr->provider == provider)
+ {
+ if (ptr->group != NULL)
+ ide_omni_search_group_set_count (ptr->group, count);
+ break;
+ }
+ }
+#endif
+}
+
+static void
+ide_omni_search_display_connect_context (IdeOmniSearchDisplay *self,
+ IdeSearchContext *context)
+{
+ const GList *providers;
+ const GList *iter;
+
+ g_return_if_fail (IDE_IS_OMNI_SEARCH_DISPLAY (self));
+ g_return_if_fail (IDE_IS_SEARCH_CONTEXT (context));
+
+ providers = ide_search_context_get_providers (context);
+
+ for (iter = providers; iter; iter = iter->next)
+ ide_omni_search_display_add_provider (self, iter->data);
+
+ g_signal_connect_object (context,
+ "result-added",
+ G_CALLBACK (ide_omni_search_display_result_added),
+ self,
+ G_CONNECT_SWAPPED);
+ g_signal_connect_object (context,
+ "result-removed",
+ G_CALLBACK (ide_omni_search_display_result_removed),
+ self,
+ G_CONNECT_SWAPPED);
+ g_signal_connect_object (context,
+ "count-set",
+ G_CALLBACK (ide_omni_search_display_count_set),
+ self,
+ G_CONNECT_SWAPPED);
+}
+
+static void
+ide_omni_search_display_disconnect_context (IdeOmniSearchDisplay *self,
+ IdeSearchContext *context)
+{
+ g_return_if_fail (IDE_IS_OMNI_SEARCH_DISPLAY (self));
+ g_return_if_fail (IDE_IS_SEARCH_CONTEXT (context));
+
+ g_signal_handlers_disconnect_by_func (context,
+ G_CALLBACK (ide_omni_search_display_result_added),
+ self);
+
+ while (self->providers->len)
+ {
+ ProviderEntry *ptr;
+
+ ptr = g_ptr_array_index (self->providers,
+ self->providers->len - 1);
+ ide_omni_search_display_remove_provider (self, ptr->provider);
+ }
+}
+
+IdeSearchContext *
+ide_omni_search_display_get_context (IdeOmniSearchDisplay *self)
+{
+ g_return_val_if_fail (IDE_IS_OMNI_SEARCH_DISPLAY (self), NULL);
+
+ return self->context;
+}
+
+void
+ide_omni_search_display_set_context (IdeOmniSearchDisplay *self,
+ IdeSearchContext *context)
+{
+ g_return_if_fail (IDE_IS_OMNI_SEARCH_DISPLAY (self));
+ g_return_if_fail (!context || IDE_IS_SEARCH_CONTEXT (context));
+
+ if (self->context != context)
+ {
+ if (self->context)
+ {
+ ide_omni_search_display_disconnect_context (self, self->context);
+ g_clear_object (&self->context);
+ }
+
+ if (context)
+ {
+ self->context = g_object_ref (context);
+ ide_omni_search_display_connect_context (self, self->context);
+ }
+
+ g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_CONTEXT]);
+ }
+}
+
+static void
+ide_omni_search_display_grab_focus (GtkWidget *widget)
+{
+ IdeOmniSearchDisplay *self = (IdeOmniSearchDisplay *)widget;
+ gsize i;
+
+ g_return_if_fail (IDE_IS_OMNI_SEARCH_DISPLAY (self));
+
+ for (i = 0; i < self->providers->len; i++)
+ {
+ ProviderEntry *ptr;
+
+ ptr = g_ptr_array_index (self->providers, i);
+
+ if (ide_omni_search_group_get_first (ptr->group))
+ {
+ gtk_widget_child_focus (GTK_WIDGET (ptr->group), GTK_DIR_DOWN);
+ break;
+ }
+ }
+}
+
+static void
+ide_omni_search_display_dispose (GObject *object)
+{
+ IdeOmniSearchDisplay *self = (IdeOmniSearchDisplay *)object;
+
+ g_clear_pointer (&self->providers, g_ptr_array_unref);
+ g_clear_object (&self->context);
+
+ G_OBJECT_CLASS (ide_omni_search_display_parent_class)->dispose (object);
+}
+
+static void
+ide_omni_search_display_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ IdeOmniSearchDisplay *self = IDE_OMNI_SEARCH_DISPLAY (object);
+
+ switch (prop_id)
+ {
+ case PROP_CONTEXT:
+ g_value_set_object (value, ide_omni_search_display_get_context (self));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+ide_omni_search_display_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ IdeOmniSearchDisplay *self = IDE_OMNI_SEARCH_DISPLAY (object);
+
+ switch (prop_id)
+ {
+ case PROP_CONTEXT:
+ ide_omni_search_display_set_context (self, g_value_get_object (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+ide_omni_search_display_class_init (IdeOmniSearchDisplayClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+ widget_class->grab_focus = ide_omni_search_display_grab_focus;
+
+ object_class->dispose = ide_omni_search_display_dispose;
+ object_class->get_property = ide_omni_search_display_get_property;
+ object_class->set_property = ide_omni_search_display_set_property;
+
+ properties [PROP_CONTEXT] =
+ g_param_spec_object ("context",
+ "Context",
+ "The active search context.",
+ IDE_TYPE_SEARCH_CONTEXT,
+ (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_properties (object_class, LAST_PROP, properties);
+
+ signals [RESULT_ACTIVATED] =
+ g_signal_new_class_handler ("result-activated",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_CALLBACK (ide_omni_search_display_real_result_activated),
+ NULL, NULL, NULL,
+ G_TYPE_NONE,
+ 1,
+ IDE_TYPE_SEARCH_RESULT);
+}
+
+static void
+ide_omni_search_display_init (IdeOmniSearchDisplay *self)
+{
+ self->providers = g_ptr_array_new_with_free_func (provider_entry_destroy);
+
+ gtk_orientable_set_orientation (GTK_ORIENTABLE (self), GTK_ORIENTATION_VERTICAL);
+}
diff --git a/libide/search/ide-omni-search-display.h b/libide/search/ide-omni-search-display.h
new file mode 100644
index 0000000..6351924
--- /dev/null
+++ b/libide/search/ide-omni-search-display.h
@@ -0,0 +1,39 @@
+/* ide-omni-search-display.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 IDE_OMNI_SEARCH_DISPLAY_H
+#define IDE_OMNI_SEARCH_DISPLAY_H
+
+#include <gtk/gtk.h>
+
+#include "ide-search-context.h"
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_OMNI_SEARCH_DISPLAY (ide_omni_search_display_get_type())
+
+G_DECLARE_FINAL_TYPE (IdeOmniSearchDisplay, ide_omni_search_display, IDE, OMNI_SEARCH_DISPLAY, GtkBin)
+
+void ide_omni_search_display_activate (IdeOmniSearchDisplay *display);
+IdeSearchContext *ide_omni_search_display_get_context (IdeOmniSearchDisplay *display);
+void ide_omni_search_display_set_context (IdeOmniSearchDisplay *display,
+ IdeSearchContext *context);
+
+G_END_DECLS
+
+#endif /* IDE_OMNI_SEARCH_DISPLAY_H */
diff --git a/libide/search/ide-omni-search-entry.c b/libide/search/ide-omni-search-entry.c
new file mode 100644
index 0000000..1248843
--- /dev/null
+++ b/libide/search/ide-omni-search-entry.c
@@ -0,0 +1,396 @@
+/* ide-omni-search-entry.c
+ *
+ * Copyright (C) 2014 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/>.
+ */
+
+#define G_LOG_DOMAIN "ide-search-box"
+
+#include <glib/gi18n.h>
+
+#include "ide-macros.h"
+#include "ide-omni-search-entry.h"
+#include "ide-omni-search-display.h"
+
+#define SHORT_DELAY_TIMEOUT_MSEC 30
+#define LONG_DELAY_TIMEOUT_MSEC 30
+
+struct _IdeOmniSearchEntry
+{
+ GtkBox parent_instance;
+
+ /* Weak references */
+ IdeWorkbench *workbench;
+ gulong set_focus_handler;
+
+ /* Template references */
+ GtkMenuButton *button;
+ IdeOmniSearchDisplay *display;
+ GtkSearchEntry *entry;
+ GtkPopover *popover;
+
+ guint delay_timeout;
+};
+
+G_DEFINE_TYPE (IdeOmniSearchEntry, ide_omni_search_entry, GTK_TYPE_BOX)
+
+GtkWidget *
+ide_omni_search_entry_new (void)
+{
+ return g_object_new (IDE_TYPE_OMNI_SEARCH_ENTRY, NULL);
+}
+
+IdeSearchEngine *
+ide_omni_search_entry_get_search_engine (IdeOmniSearchEntry *self)
+{
+ IdeContext *context;
+ IdeSearchEngine *search_engine;
+
+ g_return_val_if_fail (IDE_IS_OMNI_SEARCH_ENTRY (self), NULL);
+
+ if (self->workbench == NULL)
+ return NULL;
+
+ context = ide_workbench_get_context (self->workbench);
+ if (context == NULL)
+ return NULL;
+
+ search_engine = ide_context_get_search_engine (context);
+
+ return search_engine;
+}
+
+static gboolean
+ide_omni_search_entry_delay_cb (gpointer user_data)
+{
+ IdeOmniSearchEntry *self = user_data;
+ IdeSearchEngine *search_engine;
+ IdeSearchContext *context;
+ const gchar *search_text;
+
+ g_return_val_if_fail (IDE_IS_OMNI_SEARCH_ENTRY (self), G_SOURCE_REMOVE);
+
+ self->delay_timeout = 0;
+
+ if (self->display)
+ {
+ context = ide_omni_search_display_get_context (self->display);
+ if (context)
+ ide_search_context_cancel (context);
+
+ search_engine = ide_omni_search_entry_get_search_engine (self);
+ if (!search_engine)
+ return G_SOURCE_REMOVE;
+
+ search_text = gtk_entry_get_text (GTK_ENTRY (self->entry));
+ if (!search_text)
+ return G_SOURCE_REMOVE;
+
+ /* TODO: Remove search text */
+ context = ide_search_engine_search (search_engine, search_text);
+ ide_omni_search_display_set_context (self->display, context);
+ ide_search_context_execute (context, search_text, 7);
+ g_object_unref (context);
+ }
+
+ return G_SOURCE_REMOVE;
+}
+
+static void
+ide_omni_search_entry_popover_set_visible (IdeOmniSearchEntry *self,
+ gboolean visible)
+{
+ gboolean entry_has_text;
+
+ g_return_if_fail (IDE_IS_OMNI_SEARCH_ENTRY (self));
+
+ entry_has_text = !!(gtk_entry_get_text_length (GTK_ENTRY (self->entry)));
+
+ if (visible == gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (self->button)))
+ return;
+
+ if (visible && entry_has_text)
+ {
+ if (!gtk_widget_has_focus (GTK_WIDGET (self->entry)))
+ gtk_widget_grab_focus (GTK_WIDGET (self->entry));
+
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (self->button), TRUE);
+ }
+ else if (!visible)
+ {
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (self->button), FALSE);
+ }
+}
+
+static void
+ide_omni_search_entry_entry_activate (IdeOmniSearchEntry *self,
+ GtkSearchEntry *entry)
+{
+ g_return_if_fail (IDE_IS_OMNI_SEARCH_ENTRY (self));
+ g_return_if_fail (GTK_IS_SEARCH_ENTRY (entry));
+
+ ide_omni_search_display_activate (self->display);
+ gtk_entry_set_text (GTK_ENTRY (self->entry), "");
+}
+
+static void
+ide_omni_search_entry_entry_changed (IdeOmniSearchEntry *self,
+ GtkSearchEntry *entry)
+{
+ GtkWidget *button;
+ gboolean active;
+ gboolean sensitive;
+ guint delay_msec = SHORT_DELAY_TIMEOUT_MSEC;
+
+ g_return_if_fail (IDE_IS_OMNI_SEARCH_ENTRY (self));
+ g_return_if_fail (GTK_IS_SEARCH_ENTRY (entry));
+
+ button = GTK_WIDGET (self->button);
+ active = gtk_widget_has_focus (GTK_WIDGET (entry)) || (self->delay_timeout != 0);
+ sensitive = !!(gtk_entry_get_text_length (GTK_ENTRY (self->entry)));
+
+ if (gtk_widget_get_sensitive (button) != sensitive)
+ gtk_widget_set_sensitive (button, sensitive);
+
+ if (active)
+ ide_omni_search_entry_popover_set_visible (self, TRUE);
+
+ if (!self->delay_timeout)
+ {
+ const gchar *search_text;
+
+ search_text = gtk_entry_get_text (GTK_ENTRY (entry));
+ if (search_text)
+ {
+ if (strlen (search_text) < 3)
+ delay_msec = LONG_DELAY_TIMEOUT_MSEC;
+ self->delay_timeout = g_timeout_add (delay_msec,
+ ide_omni_search_entry_delay_cb,
+ self);
+ }
+ }
+}
+
+static gboolean
+ide_omni_search_entry_entry_key_press_event (IdeOmniSearchEntry *self,
+ GdkEventKey *key,
+ GtkSearchEntry *entry)
+{
+ g_return_val_if_fail (IDE_IS_OMNI_SEARCH_ENTRY (self), GDK_EVENT_PROPAGATE);
+ g_return_val_if_fail (key, GDK_EVENT_PROPAGATE);
+ g_return_val_if_fail (GTK_IS_SEARCH_ENTRY (entry), GDK_EVENT_PROPAGATE);
+
+ switch (key->keyval)
+ {
+ case GDK_KEY_Escape:
+ {
+ ide_omni_search_entry_popover_set_visible (self, FALSE);
+ gtk_widget_grab_focus (gtk_widget_get_toplevel (GTK_WIDGET (entry)));
+
+ return GDK_EVENT_STOP;
+ }
+ break;
+
+ case GDK_KEY_Tab:
+ case GDK_KEY_KP_Tab:
+ if ((key->state & (GDK_CONTROL_MASK | GDK_MOD1_MASK)) != 0)
+ break;
+ /* Fall through */
+ case GDK_KEY_Down:
+ case GDK_KEY_KP_Down:
+ if (gtk_widget_get_visible (GTK_WIDGET (self->popover)))
+ {
+ gtk_widget_grab_focus (GTK_WIDGET (self->display));
+ return GDK_EVENT_STOP;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ return GDK_EVENT_PROPAGATE;
+}
+
+static void
+ide_omni_search_entry_display_result_activated (IdeOmniSearchEntry *self,
+ IdeSearchResult *result,
+ IdeOmniSearchDisplay *display)
+{
+ g_return_if_fail (IDE_IS_OMNI_SEARCH_ENTRY (self));
+ g_return_if_fail (IDE_IS_SEARCH_RESULT (result));
+ g_return_if_fail (IDE_IS_OMNI_SEARCH_DISPLAY (display));
+
+ gtk_entry_set_text (GTK_ENTRY (self->entry), "");
+}
+
+static void
+ide_omni_search_entry_grab_focus (GtkWidget *widget)
+{
+ IdeOmniSearchEntry *self = (IdeOmniSearchEntry *)widget;
+
+ g_return_if_fail (IDE_IS_OMNI_SEARCH_ENTRY (self));
+
+ gtk_widget_grab_focus (GTK_WIDGET (self->entry));
+}
+
+static void
+ide_omni_search_entry_workbench_set_focus (IdeOmniSearchEntry *self,
+ GtkWidget *focus,
+ IdeWorkbench *workbench)
+{
+ g_return_if_fail (IDE_IS_OMNI_SEARCH_ENTRY (self));
+ g_return_if_fail (!focus || GTK_IS_WIDGET (focus));
+ g_return_if_fail (IDE_IS_WORKBENCH (workbench));
+
+ if (!focus ||
+ (!gtk_widget_is_ancestor (focus, GTK_WIDGET (self)) &&
+ !gtk_widget_is_ancestor (focus, GTK_WIDGET (self->popover))))
+ {
+ gtk_entry_set_text (GTK_ENTRY (self->entry), "");
+ }
+ else
+ {
+ ide_omni_search_entry_popover_set_visible (self, TRUE);
+ }
+}
+
+static void
+ide_omni_search_entry_map (GtkWidget *widget)
+{
+ IdeOmniSearchEntry *self = (IdeOmniSearchEntry *)widget;
+ GtkWidget *toplevel;
+
+ g_return_if_fail (IDE_IS_OMNI_SEARCH_ENTRY (self));
+
+ GTK_WIDGET_CLASS (ide_omni_search_entry_parent_class)->map (widget);
+
+ gtk_widget_set_sensitive (GTK_WIDGET (self->button), FALSE);
+ toplevel = gtk_widget_get_toplevel (widget);
+
+ if (IDE_IS_WORKBENCH (toplevel))
+ {
+ ide_set_weak_pointer (&self->workbench, IDE_WORKBENCH (toplevel));
+ self->set_focus_handler =
+ g_signal_connect_object (toplevel,
+ "set-focus",
+ G_CALLBACK (ide_omni_search_entry_workbench_set_focus),
+ self,
+ G_CONNECT_SWAPPED | G_CONNECT_AFTER);
+ }
+}
+
+static void
+ide_omni_search_entry_unmap (GtkWidget *widget)
+{
+ IdeOmniSearchEntry *self = (IdeOmniSearchEntry *)widget;
+
+ g_return_if_fail (IDE_IS_OMNI_SEARCH_ENTRY (self));
+
+ if (self->workbench)
+ {
+ ide_clear_signal_handler (self->workbench, &self->set_focus_handler);
+ ide_clear_weak_pointer (&self->workbench);
+ }
+
+ GTK_WIDGET_CLASS (ide_omni_search_entry_parent_class)->unmap (widget);
+}
+
+static void
+ide_omni_search_entry_constructed (GObject *object)
+{
+ IdeOmniSearchEntry *self = (IdeOmniSearchEntry *)object;
+
+ g_return_if_fail (IDE_IS_OMNI_SEARCH_ENTRY (self));
+
+ G_OBJECT_CLASS (ide_omni_search_entry_parent_class)->constructed (object);
+
+ gtk_popover_set_relative_to (self->popover, GTK_WIDGET (self->entry));
+
+ g_signal_connect_object (self->entry,
+ "activate",
+ G_CALLBACK (ide_omni_search_entry_entry_activate),
+ self,
+ G_CONNECT_SWAPPED);
+ g_signal_connect_object (self->entry,
+ "changed",
+ G_CALLBACK (ide_omni_search_entry_entry_changed),
+ self,
+ G_CONNECT_SWAPPED);
+ g_signal_connect_object (self->entry,
+ "key-press-event",
+ G_CALLBACK (ide_omni_search_entry_entry_key_press_event),
+ self,
+ G_CONNECT_SWAPPED);
+ g_signal_connect_object (self->display,
+ "result-activated",
+ G_CALLBACK (ide_omni_search_entry_display_result_activated),
+ self,
+ G_CONNECT_SWAPPED);
+}
+
+static void
+ide_omni_search_entry_finalize (GObject *object)
+{
+ IdeOmniSearchEntry *self = (IdeOmniSearchEntry *)object;
+
+ if (self->delay_timeout)
+ {
+ g_source_remove (self->delay_timeout);
+ self->delay_timeout = 0;
+ }
+
+ G_OBJECT_CLASS (ide_omni_search_entry_parent_class)->finalize (object);
+}
+
+static void
+ide_omni_search_entry_class_init (IdeOmniSearchEntryClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+ object_class->constructed = ide_omni_search_entry_constructed;
+ object_class->finalize = ide_omni_search_entry_finalize;
+
+ widget_class->grab_focus = ide_omni_search_entry_grab_focus;
+ widget_class->map = ide_omni_search_entry_map;
+ widget_class->unmap = ide_omni_search_entry_unmap;
+
+ gtk_widget_class_set_template_from_resource (widget_class,
"/org/gnome/builder/ui/ide-omni-search-entry.ui");
+ gtk_widget_class_bind_template_child (widget_class, IdeOmniSearchEntry, button);
+ gtk_widget_class_bind_template_child (widget_class, IdeOmniSearchEntry, display);
+ gtk_widget_class_bind_template_child (widget_class, IdeOmniSearchEntry, entry);
+ gtk_widget_class_bind_template_child (widget_class, IdeOmniSearchEntry, popover);
+
+ g_type_ensure (IDE_TYPE_OMNI_SEARCH_DISPLAY);
+}
+
+static void
+ide_omni_search_entry_init (IdeOmniSearchEntry *self)
+{
+ gtk_widget_init_template (GTK_WIDGET (self));
+
+ /*
+ * WORKAROUND:
+ *
+ * The GtkWidget template things that popover is a child of ours. When in
+ * reality it is a child of the GtkMenuButton (since it owns the "popover"
+ * property. Both our widget and the menu button try to call
+ * gtk_widget_destroy() on it.
+ *
+ * https://bugzilla.gnome.org/show_bug.cgi?id=741529
+ */
+ g_object_ref (self->popover);
+}
diff --git a/libide/search/ide-omni-search-entry.h b/libide/search/ide-omni-search-entry.h
new file mode 100644
index 0000000..51ff36c
--- /dev/null
+++ b/libide/search/ide-omni-search-entry.h
@@ -0,0 +1,38 @@
+/* ide-omni-search-entry.h
+ *
+ * Copyright (C) 2014 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 IDE_OMNI_SEARCH_ENTRY_H
+#define IDE_OMNI_SEARCH_ENTRY_H
+
+#include <gtk/gtk.h>
+#include <ide.h>
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_OMNI_SEARCH_ENTRY (ide_omni_search_entry_get_type())
+
+G_DECLARE_FINAL_TYPE (IdeOmniSearchEntry, ide_omni_search_entry, IDE, OMNI_SEARCH_ENTRY, GtkBox)
+
+GtkWidget *ide_omni_search_entry_new (void);
+IdeSearchEngine *ide_omni_search_entry_get_search_engine (IdeOmniSearchEntry *box);
+void ide_omni_search_entry_set_search_engine (IdeOmniSearchEntry *box,
+ IdeSearchEngine *search_engine);
+
+G_END_DECLS
+
+#endif /* IDE_OMNI_SEARCH_ENTRY_H */
diff --git a/libide/search/ide-omni-search-group.c b/libide/search/ide-omni-search-group.c
new file mode 100644
index 0000000..48b500c
--- /dev/null
+++ b/libide/search/ide-omni-search-group.c
@@ -0,0 +1,449 @@
+/* ide-omni-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 "ide-omni-search-group.h"
+#include "ide-omni-search-row.h"
+
+struct _IdeOmniSearchGroup
+{
+ GtkBox parent_instance;
+
+ /* References owned by instance */
+ IdeSearchProvider *provider;
+
+ /* References owned by template */
+ GtkListBox *rows;
+
+ guint64 count;
+};
+
+G_DEFINE_TYPE (IdeOmniSearchGroup, ide_omni_search_group, GTK_TYPE_BOX)
+
+enum {
+ PROP_0,
+ PROP_PROVIDER,
+ LAST_PROP
+};
+
+enum {
+ RESULT_ACTIVATED,
+ RESULT_SELECTED,
+ LAST_SIGNAL
+};
+
+static GQuark quarkRow;
+static GParamSpec *properties [LAST_PROP];
+static guint signals [LAST_SIGNAL];
+
+static void
+ide_omni_search_group_foreach_cb (GtkWidget *widget,
+ gpointer user_data)
+{
+ GtkWidget **row = user_data;
+
+ if (*row == NULL)
+ *row = widget;
+}
+
+IdeSearchResult *
+ide_omni_search_group_get_first (IdeOmniSearchGroup *self)
+{
+ GtkListBoxRow *row = NULL;
+ IdeSearchResult *ret = NULL;
+
+ g_return_val_if_fail (IDE_IS_OMNI_SEARCH_GROUP (self), NULL);
+
+ gtk_container_foreach (GTK_CONTAINER (self->rows),
+ ide_omni_search_group_foreach_cb,
+ &row);
+
+ if (IDE_IS_OMNI_SEARCH_ROW (row))
+ ret = ide_omni_search_row_get_result (IDE_OMNI_SEARCH_ROW (row));
+
+ return ret;
+}
+
+IdeSearchProvider *
+ide_omni_search_group_get_provider (IdeOmniSearchGroup *self)
+{
+ g_return_val_if_fail (IDE_IS_OMNI_SEARCH_GROUP (self), NULL);
+
+ return self->provider;
+}
+
+static void
+ide_omni_search_group_set_provider (IdeOmniSearchGroup *self,
+ IdeSearchProvider *provider)
+{
+ g_return_if_fail (IDE_IS_OMNI_SEARCH_GROUP (self));
+ g_return_if_fail (!provider || IDE_IS_SEARCH_PROVIDER (provider));
+
+ if (provider)
+ self->provider = g_object_ref (provider);
+}
+
+GtkWidget *
+ide_omni_search_group_create_row (IdeSearchResult *result)
+{
+ IdeSearchProvider *provider;
+ GtkWidget *row;
+
+ g_return_val_if_fail (IDE_IS_SEARCH_RESULT (result), NULL);
+
+ provider = ide_search_result_get_provider (result);
+ row = ide_search_provider_create_row (provider, result);
+ g_object_set_qdata (G_OBJECT (result), quarkRow, row);
+
+ return row;
+}
+
+void
+ide_omni_search_group_remove_result (IdeOmniSearchGroup *self,
+ IdeSearchResult *result)
+{
+ GtkWidget *row;
+
+ g_return_if_fail (IDE_IS_OMNI_SEARCH_GROUP (self));
+ g_return_if_fail (IDE_IS_SEARCH_RESULT (result));
+
+ row = g_object_get_qdata (G_OBJECT (result), quarkRow);
+
+ if (row)
+ gtk_container_remove (GTK_CONTAINER (self->rows), row);
+}
+
+void
+ide_omni_search_group_add_result (IdeOmniSearchGroup *self,
+ IdeSearchResult *result)
+{
+ GtkWidget *row;
+
+ g_return_if_fail (IDE_IS_OMNI_SEARCH_GROUP (self));
+ g_return_if_fail (IDE_IS_SEARCH_RESULT (result));
+
+ row = ide_omni_search_group_create_row (result);
+ gtk_container_add (GTK_CONTAINER (self->rows), row);
+
+ gtk_list_box_invalidate_sort (self->rows);
+
+ self->count++;
+}
+
+static gint
+compare_cb (GtkListBoxRow *row1,
+ GtkListBoxRow *row2,
+ gpointer user_data)
+{
+ IdeSearchResult *result1;
+ IdeSearchResult *result2;
+ gfloat score1;
+ gfloat score2;
+
+ result1 = ide_omni_search_row_get_result (IDE_OMNI_SEARCH_ROW (row1));
+ result2 = ide_omni_search_row_get_result (IDE_OMNI_SEARCH_ROW (row2));
+
+ score1 = ide_search_result_get_score (result1);
+ score2 = ide_search_result_get_score (result2);
+
+ if (score1 < score2)
+ return 1;
+ else if (score1 > score2)
+ return -1;
+ else
+ return 0;
+}
+
+static void
+ide_omni_search_group_result_activated (IdeOmniSearchGroup *self,
+ GtkWidget *widget,
+ IdeSearchResult *result)
+{
+ IdeSearchProvider *provider;
+
+ g_return_if_fail (IDE_IS_OMNI_SEARCH_GROUP (self));
+ g_return_if_fail (GTK_IS_WIDGET (widget));
+ g_return_if_fail (IDE_IS_SEARCH_RESULT (result));
+
+ provider = ide_search_result_get_provider (result);
+ ide_search_provider_activate (provider, widget, result);
+}
+
+void
+ide_omni_search_group_unselect (IdeOmniSearchGroup *self)
+{
+ g_return_if_fail (IDE_IS_OMNI_SEARCH_GROUP (self));
+
+ gtk_list_box_unselect_all (self->rows);
+}
+
+static void
+ide_omni_search_group_row_activated (IdeOmniSearchGroup *self,
+ GtkListBoxRow *row,
+ GtkListBox *list_box)
+{
+ IdeSearchResult *result;
+
+ g_return_if_fail (IDE_IS_OMNI_SEARCH_GROUP (self));
+ g_return_if_fail (IDE_IS_OMNI_SEARCH_ROW (row));
+ g_return_if_fail (GTK_IS_LIST_BOX (list_box));
+
+ result = ide_omni_search_row_get_result (IDE_OMNI_SEARCH_ROW (row));
+ if (result)
+ g_signal_emit (self, signals [RESULT_ACTIVATED], 0, row, result);
+}
+
+static void
+ide_omni_search_group_row_selected (IdeOmniSearchGroup *self,
+ GtkListBoxRow *row,
+ GtkListBox *list_box)
+{
+ GtkWidget *child;
+
+ g_return_if_fail (IDE_IS_OMNI_SEARCH_GROUP (self));
+ g_return_if_fail (!row || GTK_IS_LIST_BOX_ROW (row));
+ g_return_if_fail (GTK_IS_LIST_BOX (list_box));
+
+ if (row)
+ {
+ child = gtk_bin_get_child (GTK_BIN (row));
+
+ if (IDE_IS_OMNI_SEARCH_ROW (child))
+ {
+ IdeSearchResult *result;
+
+ result = ide_omni_search_row_get_result (IDE_OMNI_SEARCH_ROW (child));
+ if (result)
+ g_signal_emit (self, signals [RESULT_SELECTED], 0, result);
+ }
+ }
+}
+
+void
+ide_omni_search_group_focus_first (IdeOmniSearchGroup *self)
+{
+ GtkListBoxRow *row;
+
+ g_return_if_fail (IDE_IS_OMNI_SEARCH_GROUP (self));
+
+ row = gtk_list_box_get_row_at_y (self->rows, 1);
+
+ if (row)
+ {
+ gtk_list_box_unselect_all (self->rows);
+ gtk_widget_child_focus (GTK_WIDGET (self->rows), GTK_DIR_DOWN);
+ }
+}
+
+void
+ide_omni_search_group_focus_last (IdeOmniSearchGroup *self)
+{
+ GtkAllocation alloc;
+ GtkListBoxRow *row;
+
+ g_return_if_fail (IDE_IS_OMNI_SEARCH_GROUP (self));
+
+ gtk_widget_get_allocation (GTK_WIDGET (self->rows), &alloc);
+ row = gtk_list_box_get_row_at_y (self->rows, alloc.height - 2);
+
+ if (row)
+ {
+ gtk_list_box_unselect_all (self->rows);
+ gtk_widget_child_focus (GTK_WIDGET (self->rows), GTK_DIR_UP);
+ }
+}
+
+static void
+ide_omni_search_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
+ide_omni_search_group_keynav_failed (IdeOmniSearchGroup *self,
+ GtkDirectionType dir,
+ GtkListBox *list_box)
+{
+ gboolean ret = FALSE;
+
+ g_return_val_if_fail (IDE_IS_OMNI_SEARCH_GROUP (self), FALSE);
+ g_return_val_if_fail (GTK_IS_LIST_BOX (list_box), FALSE);
+
+ g_signal_emit_by_name (self, "keynav-failed", dir, &ret);
+
+ return ret;
+}
+
+static void
+ide_omni_search_group_finalize (GObject *object)
+{
+ IdeOmniSearchGroup *self = (IdeOmniSearchGroup *)object;
+
+ g_clear_object (&self->provider);
+
+ G_OBJECT_CLASS (ide_omni_search_group_parent_class)->finalize (object);
+}
+
+static void
+ide_omni_search_group_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ IdeOmniSearchGroup *self = IDE_OMNI_SEARCH_GROUP (object);
+
+ switch (prop_id)
+ {
+ case PROP_PROVIDER:
+ g_value_set_object (value, ide_omni_search_group_get_provider (self));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+ide_omni_search_group_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ IdeOmniSearchGroup *self = IDE_OMNI_SEARCH_GROUP (object);
+
+ switch (prop_id)
+ {
+ case PROP_PROVIDER:
+ ide_omni_search_group_set_provider (self, g_value_get_object (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+ide_omni_search_group_class_init (IdeOmniSearchGroupClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+ object_class->finalize = ide_omni_search_group_finalize;
+ object_class->get_property = ide_omni_search_group_get_property;
+ object_class->set_property = ide_omni_search_group_set_property;
+
+ properties [PROP_PROVIDER] =
+ g_param_spec_object ("provider",
+ "Provider",
+ "The search provider",
+ IDE_TYPE_SEARCH_PROVIDER,
+ (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_properties (object_class, LAST_PROP, properties);
+
+ signals [RESULT_ACTIVATED] =
+ g_signal_new_class_handler ("result-activated",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_CALLBACK (ide_omni_search_group_result_activated),
+ NULL, NULL, NULL,
+ G_TYPE_NONE,
+ 2,
+ GTK_TYPE_WIDGET,
+ IDE_TYPE_SEARCH_RESULT);
+
+ signals [RESULT_SELECTED] =
+ g_signal_new ("result-selected",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ 0,
+ NULL, NULL, NULL,
+ G_TYPE_NONE,
+ 1,
+ IDE_TYPE_SEARCH_RESULT);
+
+ gtk_widget_class_set_template_from_resource (widget_class,
"/org/gnome/builder/ui/ide-omni-search-group.ui");
+ gtk_widget_class_bind_template_child (widget_class, IdeOmniSearchGroup, rows);
+
+ quarkRow = g_quark_from_static_string ("IDE_OMNI_SEARCH_ROW");
+}
+
+static void
+ide_omni_search_group_init (IdeOmniSearchGroup *self)
+{
+ gtk_widget_init_template (GTK_WIDGET (self));
+
+ g_signal_connect_object (self->rows,
+ "keynav-failed",
+ G_CALLBACK (ide_omni_search_group_keynav_failed),
+ self,
+ G_CONNECT_SWAPPED);
+ g_signal_connect_object (self->rows,
+ "row-activated",
+ G_CALLBACK (ide_omni_search_group_row_activated),
+ self,
+ G_CONNECT_SWAPPED);
+ g_signal_connect_object (self->rows,
+ "row-selected",
+ G_CALLBACK (ide_omni_search_group_row_selected),
+ self,
+ G_CONNECT_SWAPPED);
+
+ gtk_list_box_set_sort_func (self->rows, compare_cb, NULL, NULL);
+ gtk_list_box_set_header_func (self->rows, ide_omni_search_group_header_cb, NULL, NULL);
+}
+
+gboolean
+ide_omni_search_group_activate (IdeOmniSearchGroup *group)
+{
+ GtkListBoxRow *row = NULL;
+
+ g_return_val_if_fail (IDE_IS_OMNI_SEARCH_GROUP (group), FALSE);
+
+ gtk_container_foreach (GTK_CONTAINER (group->rows),
+ ide_omni_search_group_foreach_cb,
+ &row);
+
+ if (IDE_IS_OMNI_SEARCH_ROW (row))
+ {
+ IdeSearchResult *result;
+ IdeSearchProvider *provider;
+
+ result = ide_omni_search_row_get_result (IDE_OMNI_SEARCH_ROW (row));
+ provider = ide_search_result_get_provider (result);
+ ide_search_provider_activate (provider, GTK_WIDGET (row), result);
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
diff --git a/libide/search/ide-omni-search-group.h b/libide/search/ide-omni-search-group.h
new file mode 100644
index 0000000..2ff1464
--- /dev/null
+++ b/libide/search/ide-omni-search-group.h
@@ -0,0 +1,45 @@
+/* ide-omni-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 IDE_OMNI_SEARCH_GROUP_H
+#define IDE_OMNI_SEARCH_GROUP_H
+
+#include <gtk/gtk.h>
+#include <ide.h>
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_OMNI_SEARCH_GROUP (ide_omni_search_group_get_type())
+
+G_DECLARE_FINAL_TYPE (IdeOmniSearchGroup, ide_omni_search_group, IDE, OMNI_SEARCH_GROUP, GtkBox)
+
+void ide_omni_search_group_clear (IdeOmniSearchGroup *group);
+IdeSearchProvider *ide_omni_search_group_get_provider (IdeOmniSearchGroup *group);
+void ide_omni_search_group_add_result (IdeOmniSearchGroup *group,
+ IdeSearchResult *result);
+void ide_omni_search_group_remove_result (IdeOmniSearchGroup *group,
+ IdeSearchResult *result);
+void ide_omni_search_group_unselect (IdeOmniSearchGroup *group);
+void ide_omni_search_group_focus_first (IdeOmniSearchGroup *group);
+void ide_omni_search_group_focus_last (IdeOmniSearchGroup *group);
+IdeSearchResult *ide_omni_search_group_get_first (IdeOmniSearchGroup *group);
+gboolean ide_omni_search_group_activate (IdeOmniSearchGroup *group);
+
+G_END_DECLS
+
+#endif /* IDE_OMNI_SEARCH_GROUP_H */
diff --git a/libide/search/ide-omni-search-row.c b/libide/search/ide-omni-search-row.c
new file mode 100644
index 0000000..4a50a1f
--- /dev/null
+++ b/libide/search/ide-omni-search-row.c
@@ -0,0 +1,184 @@
+/* ide-omni-search-row.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 "ide-omni-search-row.h"
+
+struct _IdeOmniSearchRow
+{
+ GtkBox parent_instance;
+
+ IdeSearchResult *result;
+
+ /* References owned by template */
+ GtkLabel *title;
+ GtkImage *image;
+};
+
+G_DEFINE_TYPE (IdeOmniSearchRow, ide_omni_search_row, GTK_TYPE_LIST_BOX_ROW)
+
+enum {
+ PROP_0,
+ PROP_ICON_NAME,
+ PROP_RESULT,
+ LAST_PROP
+};
+
+static GParamSpec *properties [LAST_PROP];
+
+static void
+ide_omni_search_row_set_icon_name (IdeOmniSearchRow *self,
+ const gchar *icon_name)
+{
+ g_assert (IDE_IS_OMNI_SEARCH_ROW (self));
+
+ gtk_image_set_from_icon_name (self->image, icon_name, GTK_ICON_SIZE_MENU);
+}
+
+static void
+ide_omni_search_row_connect (IdeOmniSearchRow *row,
+ IdeSearchResult *result)
+{
+ const gchar *title;
+
+ g_return_if_fail (IDE_IS_OMNI_SEARCH_ROW (row));
+ g_return_if_fail (IDE_IS_SEARCH_RESULT (result));
+
+ title = ide_search_result_get_title (result);
+ gtk_label_set_markup (row->title, title);
+}
+
+IdeSearchResult *
+ide_omni_search_row_get_result (IdeOmniSearchRow *row)
+{
+ g_return_val_if_fail (IDE_IS_OMNI_SEARCH_ROW (row), NULL);
+
+ return row->result;
+}
+
+void
+ide_omni_search_row_set_result (IdeOmniSearchRow *row,
+ IdeSearchResult *result)
+{
+ g_return_if_fail (IDE_IS_OMNI_SEARCH_ROW (row));
+ g_return_if_fail (IDE_IS_SEARCH_RESULT (result));
+
+ if (result != row->result)
+ {
+ g_clear_object (&row->result);
+
+ if (result)
+ {
+ row->result = g_object_ref (result);
+ ide_omni_search_row_connect (row, result);
+ }
+
+ g_object_notify_by_pspec (G_OBJECT (row), properties [PROP_RESULT]);
+ }
+}
+
+static void
+ide_omni_search_row_finalize (GObject *object)
+{
+ IdeOmniSearchRow *self = (IdeOmniSearchRow *)object;
+
+ g_clear_object (&self->result);
+
+ G_OBJECT_CLASS (ide_omni_search_row_parent_class)->finalize (object);
+}
+
+static void
+ide_omni_search_row_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ IdeOmniSearchRow *self = IDE_OMNI_SEARCH_ROW (object);
+
+ switch (prop_id)
+ {
+ case PROP_RESULT:
+ g_value_set_object (value, ide_omni_search_row_get_result (self));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+ide_omni_search_row_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ IdeOmniSearchRow *self = IDE_OMNI_SEARCH_ROW (object);
+
+ switch (prop_id)
+ {
+ case PROP_ICON_NAME:
+ ide_omni_search_row_set_icon_name (self, g_value_get_string (value));
+ break;
+
+ case PROP_RESULT:
+ ide_omni_search_row_set_result (self, g_value_get_object (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+ide_omni_search_row_class_init (IdeOmniSearchRowClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+ object_class->finalize = ide_omni_search_row_finalize;
+ object_class->get_property = ide_omni_search_row_get_property;
+ object_class->set_property = ide_omni_search_row_set_property;
+
+ properties [PROP_ICON_NAME] =
+ g_param_spec_string ("icon-name",
+ "Icon Name",
+ "Icon Name",
+ NULL,
+ (G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
+
+ properties [PROP_RESULT] =
+ g_param_spec_object ("result",
+ "Result",
+ "Result",
+ IDE_TYPE_SEARCH_RESULT,
+ (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_properties (object_class, LAST_PROP, properties);
+
+ gtk_widget_class_set_css_name (widget_class, "omnisearchrow");
+ gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/builder/ui/ide-omni-search-row.ui");
+ gtk_widget_class_bind_template_child (widget_class, IdeOmniSearchRow, image);
+ gtk_widget_class_bind_template_child (widget_class, IdeOmniSearchRow, title);
+}
+
+static void
+ide_omni_search_row_init (IdeOmniSearchRow *self)
+{
+ gtk_widget_init_template (GTK_WIDGET (self));
+}
diff --git a/libide/search/ide-omni-search-row.h b/libide/search/ide-omni-search-row.h
new file mode 100644
index 0000000..2e950ec
--- /dev/null
+++ b/libide/search/ide-omni-search-row.h
@@ -0,0 +1,38 @@
+/* ide-omni-search-row.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 IDE_OMNI_SEARCH_ROW_H
+#define IDE_OMNI_SEARCH_ROW_H
+
+#include <gtk/gtk.h>
+
+#include "ide-search-result.h"
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_OMNI_SEARCH_ROW (ide_omni_search_row_get_type())
+
+G_DECLARE_FINAL_TYPE (IdeOmniSearchRow, ide_omni_search_row, IDE, OMNI_SEARCH_ROW, GtkListBoxRow)
+
+IdeSearchResult *ide_omni_search_row_get_result (IdeOmniSearchRow *row);
+void ide_omni_search_row_set_result (IdeOmniSearchRow *row,
+ IdeSearchResult *result);
+
+G_END_DECLS
+
+#endif /* IDE_OMNI_SEARCH_ROW_H */
diff --git a/plugins/file-search/gb-file-search-index.c b/plugins/file-search/gb-file-search-index.c
index 873d734..c85406e 100644
--- a/plugins/file-search/gb-file-search-index.c
+++ b/plugins/file-search/gb-file-search-index.c
@@ -22,7 +22,6 @@
#include "gb-file-search-index.h"
#include "gb-file-search-result.h"
-#include "gb-string.h"
struct _GbFileSearchIndex
{
@@ -325,8 +324,7 @@ gb_file_search_index_populate (GbFileSearchIndex *self,
g_autoptr(GbFileSearchResult) result = NULL;
g_autofree gchar *markup = NULL;
- markup = gb_str_highlight_full (match->key, query, TRUE,
- (GB_HIGHLIGHT_BOLD | GB_HIGHLIGHT_UNDERLINE));
+ markup = ide_completion_item_fuzzy_highlight (match->key, query);
result = g_object_new (GB_TYPE_FILE_SEARCH_RESULT,
"context", icontext,
"provider", provider,
diff --git a/plugins/file-search/gb-file-search-provider.c b/plugins/file-search/gb-file-search-provider.c
index 60f04da..e25280e 100644
--- a/plugins/file-search/gb-file-search-provider.c
+++ b/plugins/file-search/gb-file-search-provider.c
@@ -17,12 +17,11 @@
*/
#include <glib/gi18n.h>
+#include <ide.h>
#include <libpeas/peas.h>
#include "gb-file-search-provider.h"
#include "gb-file-search-index.h"
-#include "gb-search-display-row.h"
-#include "gb-workbench.h"
struct _GbFileSearchProvider
{
@@ -91,7 +90,8 @@ gb_file_search_provider_create_row (IdeSearchProvider *provider,
g_assert (IDE_IS_SEARCH_PROVIDER (provider));
g_assert (IDE_IS_SEARCH_RESULT (result));
- return g_object_new (GB_TYPE_SEARCH_DISPLAY_ROW,
+ return g_object_new (IDE_TYPE_OMNI_SEARCH_ROW,
+ "icon-name", "text-x-generic-symbolic",
"result", result,
"visible", TRUE,
NULL);
@@ -110,7 +110,7 @@ gb_file_search_provider_activate (IdeSearchProvider *provider,
toplevel = gtk_widget_get_toplevel (row);
- if (GB_IS_WORKBENCH (toplevel))
+ if (IDE_IS_WORKBENCH (toplevel))
{
g_autofree gchar *path = NULL;
g_autoptr(GFile) file = NULL;
@@ -118,13 +118,13 @@ gb_file_search_provider_activate (IdeSearchProvider *provider,
IdeVcs *vcs;
GFile *workdir;
- context = gb_workbench_get_context (GB_WORKBENCH (toplevel));
+ context = ide_workbench_get_context (IDE_WORKBENCH (toplevel));
vcs = ide_context_get_vcs (context);
workdir = ide_vcs_get_working_directory (vcs);
g_object_get (result, "path", &path, NULL);
file = g_file_get_child (workdir, path);
- gb_workbench_open (GB_WORKBENCH (toplevel), file);
+ ide_workbench_open_files_async (IDE_WORKBENCH (toplevel), &file, 1, NULL, NULL, NULL, NULL);
}
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]