[gnome-software] Add a search shell and use the plugins to return results
- From: Richard Hughes <rhughes src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-software] Add a search shell and use the plugins to return results
- Date: Thu, 29 Aug 2013 15:38:28 +0000 (UTC)
commit 209e261d085a77f94f7b3b15cbbb9a1ced654c0f
Author: Richard Hughes <richard hughsie com>
Date: Thu Aug 29 09:52:02 2013 +0100
Add a search shell and use the plugins to return results
src/Makefile.am | 2 +
src/gnome-software.ui | 35 +++
src/gs-shell-category.c | 3 +
src/gs-shell-details.c | 3 +
src/gs-shell-overview.c | 27 +++-
src/gs-shell-search.c | 549 +++++++++++++++++++++++++++++++++++++++++++++++
src/gs-shell-search.h | 62 ++++++
src/gs-shell-updates.c | 3 +
src/gs-shell.c | 16 ++
src/gs-shell.h | 1 +
10 files changed, 699 insertions(+), 2 deletions(-)
---
diff --git a/src/Makefile.am b/src/Makefile.am
index 0e45541..403e0e5 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -50,6 +50,8 @@ gnome_software_SOURCES = \
gs-shell-overview.h \
gs-shell-updates.c \
gs-shell-updates.h \
+ gs-shell-search.c \
+ gs-shell-search.h \
gs-plugin-loader.c \
gs-plugin-loader.h \
gs-plugin-loader-sync.c \
diff --git a/src/gnome-software.ui b/src/gnome-software.ui
index 09a0ad0..7fd23c4 100644
--- a/src/gnome-software.ui
+++ b/src/gnome-software.ui
@@ -486,6 +486,41 @@
</object>
</child>
<child>
+ <object class="GtkOverlay" id="overlay_search">
+ <property name="visible">True</property>
+ <child type="overlay">
+ <object class="GtkSpinner" id="spinner_search">
+ <property name="width_request">128</property>
+ <property name="height_request">128</property>
+ <property name="halign">center</property>
+ <property name="valign">center</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow" id="scrolledwindow_search">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">never</property>
+ <property name="vscrollbar_policy">automatic</property>
+ <property name="shadow_type">none</property>
+ <style>
+ <class name="main-scrolled-software"/>
+ </style>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="tab">
+ <object class="GtkLabel" id="label_tab_search">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label">Search</property>
+ </object>
+ </child>
+ <child>
<object class="GtkOverlay" id="overlay_updates">
<property name="visible">True</property>
<child type="overlay">
diff --git a/src/gs-shell-category.c b/src/gs-shell-category.c
index 15146cb..b51d19d 100644
--- a/src/gs-shell-category.c
+++ b/src/gs-shell-category.c
@@ -50,6 +50,9 @@ gs_shell_category_refresh (GsShellCategory *shell)
widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "application_details_header"));
gtk_widget_show (widget);
gtk_label_set_label (GTK_LABEL (widget), priv->category);
+
+ widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "search_bar"));
+ gtk_widget_hide (widget);
}
static void
diff --git a/src/gs-shell-details.c b/src/gs-shell-details.c
index e69e3e7..122d109 100644
--- a/src/gs-shell-details.c
+++ b/src/gs-shell-details.c
@@ -70,6 +70,9 @@ gs_shell_details_refresh (GsShellDetails *shell_details)
widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "button_back"));
gtk_widget_show (widget);
+ widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "search_bar"));
+ gtk_widget_hide (widget);
+
kind = gs_app_get_kind (priv->app);
state = gs_app_get_state (priv->app);
diff --git a/src/gs-shell-overview.c b/src/gs-shell-overview.c
index d05a106..1b0acad 100644
--- a/src/gs-shell-overview.c
+++ b/src/gs-shell-overview.c
@@ -196,6 +196,7 @@ gs_shell_overview_get_featured_cb (GObject *source_object,
GsPluginLoader *plugin_loader = GS_PLUGIN_LOADER (source_object);
GtkImage *image;
GtkWidget *button;
+ GtkWidget *widget;
list = gs_plugin_loader_get_featured_finish (plugin_loader,
res,
@@ -216,11 +217,9 @@ gs_shell_overview_get_featured_cb (GObject *source_object,
g_signal_connect (button, "clicked",
G_CALLBACK (app_tile_clicked), shell_overview);
-#ifdef SEARCH
/* focus back to the text extry */
widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "entry_search"));
gtk_widget_grab_focus (widget);
-#endif
out:
g_list_free (list);
return;
@@ -262,11 +261,20 @@ gs_shell_overview_refresh (GsShellOverview *shell_overview)
widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "buttonbox_main"));
gtk_widget_show (widget);
+ widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "search_bar"));
+ gtk_widget_show (widget);
/* no need to refresh */
if (priv->cache_valid)
return;
+ widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "search_bar"));
+ gtk_widget_show (widget);
+
+ /* clear search items */
+ widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "entry_search"));
+ gtk_entry_set_text (GTK_ENTRY (widget), "");
+
grid = GTK_WIDGET (gtk_builder_get_object (priv->builder, "box_popular"));
container_remove_all (GTK_CONTAINER (grid));
@@ -291,6 +299,16 @@ gs_shell_overview_refresh (GsShellOverview *shell_overview)
}
/**
+ * gs_shell_overview_search_activated_cb:
+ */
+static void
+gs_shell_overview_search_activated_cb (GtkEntry *entry, GsShellOverview *shell_overview)
+{
+ GsShellOverviewPrivate *priv = shell_overview->priv;
+ gs_shell_set_mode (priv->shell, GS_SHELL_MODE_SEARCH);
+}
+
+/**
* gs_shell_overview_setup:
*/
void
@@ -301,6 +319,7 @@ gs_shell_overview_setup (GsShellOverview *shell_overview,
GCancellable *cancellable)
{
GsShellOverviewPrivate *priv = shell_overview->priv;
+ GtkWidget *widget;
g_return_if_fail (GS_IS_SHELL_OVERVIEW (shell_overview));
@@ -308,6 +327,10 @@ gs_shell_overview_setup (GsShellOverview *shell_overview,
priv->builder = g_object_ref (builder);
priv->cancellable = g_object_ref (cancellable);
+ widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "entry_search"));
+ g_signal_connect (GTK_EDITABLE (widget), "activate",
+ G_CALLBACK (gs_shell_overview_search_activated_cb), shell_overview);
+
/* avoid a ref cycle */
priv->shell = shell;
}
diff --git a/src/gs-shell-search.c b/src/gs-shell-search.c
new file mode 100644
index 0000000..dc9f6e2
--- /dev/null
+++ b/src/gs-shell-search.c
@@ -0,0 +1,549 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2013 Richard Hughes <richard hughsie com>
+ *
+ * Licensed under the GNU General Public License Version 2
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+
+#include <string.h>
+#include <glib/gi18n.h>
+
+#include "gs-shell-search.h"
+#include "gs-app.h"
+#include "gs-utils.h"
+#include "gs-app-widget.h"
+
+static void gs_shell_search_finalize (GObject *object);
+
+#define GS_SHELL_SEARCH_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GS_TYPE_SHELL_SEARCH,
GsShellSearchPrivate))
+
+struct GsShellSearchPrivate
+{
+ GsPluginLoader *plugin_loader;
+ GtkBuilder *builder;
+ GCancellable *cancellable;
+ GtkListBox *list_box_search;
+ GtkSizeGroup *sizegroup_image;
+ GtkSizeGroup *sizegroup_name;
+ gboolean waiting;
+};
+
+G_DEFINE_TYPE (GsShellSearch, gs_shell_search, G_TYPE_OBJECT)
+
+/**
+ * _gtk_container_remove_all_cb:
+ **/
+static void
+_gtk_container_remove_all_cb (GtkWidget *widget, gpointer user_data)
+{
+ GtkContainer *container = GTK_CONTAINER (user_data);
+ gtk_container_remove (container, widget);
+}
+
+/**
+ * _gtk_container_remove_all:
+ **/
+static void
+_gtk_container_remove_all (GtkContainer *container)
+{
+ gtk_container_foreach (container,
+ _gtk_container_remove_all_cb,
+ container);
+}
+
+static void
+gs_shell_search_app_widget_activated_cb (GtkListBox *list_box,
+ GtkListBoxRow *row,
+ GsShellSearch *shell_search)
+{
+ const gchar *tmp;
+ GsApp *app;
+ GtkWidget *details, *button, *grid;
+ GtkWidget *image, *label;
+ PangoAttrList *attr_list;
+ GsAppWidget *app_widget;
+
+ app_widget = GS_APP_WIDGET (gtk_bin_get_child (GTK_BIN (row)));
+ app = gs_app_widget_get_app (app_widget);
+
+ details = gtk_dialog_new_with_buttons (_("Details"),
+ GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (list_box))),
+ GTK_DIALOG_MODAL,
+ _("_Done"), GTK_RESPONSE_CLOSE,
+ NULL);
+ gtk_container_set_border_width (GTK_CONTAINER (details), 20);
+ button = gtk_dialog_get_widget_for_response (GTK_DIALOG (details), GTK_RESPONSE_CLOSE);
+ gtk_style_context_add_class (gtk_widget_get_style_context (button), "suggested-action");
+ g_signal_connect_swapped (button, "clicked", G_CALLBACK (gtk_widget_destroy), details);
+
+ grid = gtk_grid_new ();
+ gtk_widget_show (grid);
+ gtk_widget_set_halign (grid, GTK_ALIGN_FILL);
+ gtk_grid_set_column_spacing (GTK_GRID (grid), 20);
+ gtk_container_add (GTK_CONTAINER (gtk_dialog_get_content_area (GTK_DIALOG (details))), grid);
+
+ image = gtk_image_new ();
+ if (gs_app_get_pixbuf (app)) {
+ gtk_image_set_from_pixbuf (GTK_IMAGE (image), gs_app_get_pixbuf (app)); gtk_widget_show
(image);
+ }
+ gtk_grid_attach (GTK_GRID (grid), image, 0, 0, 1, 3);
+
+ label = gtk_label_new (gs_app_get_name (app));
+ gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
+ gtk_widget_set_hexpand (label, TRUE);
+ gtk_widget_set_margin_bottom (label, 10);
+ attr_list = pango_attr_list_new ();
+ pango_attr_list_insert (attr_list, pango_attr_weight_new (PANGO_WEIGHT_BOLD));
+ pango_attr_list_insert (attr_list, pango_attr_scale_new (1));
+ gtk_label_set_attributes (GTK_LABEL (label), attr_list);
+ pango_attr_list_unref (attr_list);
+ gtk_widget_show (label);
+ gtk_grid_attach (GTK_GRID (grid), label, 1, 0, 1, 1);
+
+ label = gtk_label_new (NULL);
+ gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
+ gtk_widget_set_hexpand (label, TRUE);
+ gtk_widget_set_margin_bottom (label, 20);
+ if (gs_app_get_summary (app)) {
+ gtk_label_set_label (GTK_LABEL (label), gs_app_get_summary (app));
+ gtk_widget_show (label);
+ }
+ gtk_grid_attach (GTK_GRID (grid), label, 1, 1, 1, 1);
+ tmp = gs_app_get_description (app);
+ label = gtk_label_new (tmp);
+ gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
+ gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
+ gtk_widget_show (label);
+ gtk_grid_attach (GTK_GRID (grid), label, 0, 3, 2, 1);
+
+ if (gs_app_get_url (app)) {
+ button = gtk_link_button_new_with_label (gs_app_get_url (app), _("Visit website"));
+ gtk_widget_set_halign (button, GTK_ALIGN_START);
+ gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE);
+ gtk_widget_show (button);
+ gtk_grid_attach (GTK_GRID (grid), button, 0, 4, 2, 1);
+ }
+
+ gtk_window_present (GTK_WINDOW (details));
+}
+
+/**
+ * gs_shell_search_finished_func:
+ **/
+static void
+gs_shell_search_finished_func (GsPluginLoader *plugin_loader, GsApp *app, gpointer user_data)
+{
+}
+
+/**
+ * gs_shell_search_app_remove:
+ **/
+static void
+gs_shell_search_app_remove (GsShellSearch *shell_search, GsApp *app)
+{
+ GsShellSearchPrivate *priv = shell_search->priv;
+ GString *markup;
+ GtkResponseType response;
+ GtkWidget *dialog;
+ GtkWindow *window;
+
+ window = GTK_WINDOW (gtk_builder_get_object (priv->builder, "window_software"));
+ markup = g_string_new ("");
+ g_string_append_printf (markup,
+ _("Are you sure you want to remove %s?"),
+ gs_app_get_name (app));
+ g_string_prepend (markup, "<b>");
+ g_string_append (markup, "</b>");
+ dialog = gtk_message_dialog_new (window,
+ GTK_DIALOG_MODAL,
+ GTK_MESSAGE_QUESTION,
+ GTK_BUTTONS_CANCEL,
+ NULL);
+ gtk_message_dialog_set_markup (GTK_MESSAGE_DIALOG (dialog), markup->str);
+ gtk_message_dialog_format_secondary_markup (GTK_MESSAGE_DIALOG (dialog),
+ _("%s will be removed, and you will have to install it to
use it again."),
+ gs_app_get_name (app));
+ gtk_dialog_add_button (GTK_DIALOG (dialog), _("Remove"), GTK_RESPONSE_OK);
+ response = gtk_dialog_run (GTK_DIALOG (dialog));
+ if (response == GTK_RESPONSE_OK) {
+ g_debug ("remove %s", gs_app_get_id (app));
+ gs_plugin_loader_app_remove (priv->plugin_loader,
+ app,
+ priv->cancellable,
+ gs_shell_search_finished_func,
+ shell_search);
+ }
+ g_string_free (markup, TRUE);
+ gtk_widget_destroy (dialog);
+}
+
+/**
+ * gs_shell_search_app_install:
+ **/
+static void
+gs_shell_search_app_install (GsShellSearch *shell_search, GsApp *app)
+{
+ GsShellSearchPrivate *priv = shell_search->priv;
+ gs_plugin_loader_app_install (priv->plugin_loader,
+ app,
+ priv->cancellable,
+ gs_shell_search_finished_func,
+ shell_search);
+}
+
+/**
+ * gs_shell_search_app_widget_clicked_cb:
+ **/
+static void
+gs_shell_search_app_widget_clicked_cb (GsAppWidget *app_widget,
+ GsShellSearch *shell_search)
+{
+ GsApp *app;
+ app = gs_app_widget_get_app (app_widget);
+ if (gs_app_get_state (app) == GS_APP_STATE_AVAILABLE)
+ gs_shell_search_app_install (shell_search, app);
+ else if (gs_app_get_state (app) == GS_APP_STATE_INSTALLED)
+ gs_shell_search_app_remove (shell_search, app);
+}
+
+/**
+ * gs_shell_search_get_search_cb:
+ **/
+static void
+gs_shell_search_get_search_cb (GObject *source_object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ GError *error = NULL;
+ GList *l;
+ GList *list;
+ GsApp *app;
+ GsShellSearch *shell_search = GS_SHELL_SEARCH (user_data);
+ GsShellSearchPrivate *priv = shell_search->priv;
+ GsPluginLoader *plugin_loader = GS_PLUGIN_LOADER (source_object);
+ GtkWidget *widget;
+
+ widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "spinner_search"));
+ gs_stop_spinner (GTK_SPINNER (widget));
+
+ priv->waiting = FALSE;
+
+ list = gs_plugin_loader_search_finish (plugin_loader,
+ res,
+ &error);
+ if (list == NULL) {
+ g_warning ("failed to get search apps: %s", error->message);
+ g_error_free (error);
+ goto out;
+ }
+ for (l = list; l != NULL; l = l->next) {
+ app = GS_APP (l->data);
+ g_debug ("adding search %s", gs_app_get_id (app));
+ widget = gs_app_widget_new ();
+ g_signal_connect (widget, "button-clicked",
+ G_CALLBACK (gs_shell_search_app_widget_clicked_cb),
+ shell_search);
+ gs_app_widget_set_app (GS_APP_WIDGET (widget), app);
+ gtk_container_add (GTK_CONTAINER (priv->list_box_search), widget);
+ gs_app_widget_set_size_groups (GS_APP_WIDGET (widget),
+ priv->sizegroup_image,
+ priv->sizegroup_name);
+ gtk_widget_show (widget);
+ }
+
+ /* focus back to the text extry */
+ widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "entry_search"));
+ gtk_widget_grab_focus (widget);
+out: ;
+}
+
+/**
+ * gs_shell_search_refresh:
+ **/
+void
+gs_shell_search_refresh (GsShellSearch *shell_search, const gchar *value)
+{
+ GsShellSearchPrivate *priv = shell_search->priv;
+ GtkWidget *widget;
+ GtkSpinner *spinner;
+
+ widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "buttonbox_main"));
+ gtk_widget_show (widget);
+
+ widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "search_bar"));
+ gtk_widget_show (widget);
+
+ if (priv->waiting)
+ return;
+
+ /* remove old entries */
+ _gtk_container_remove_all (GTK_CONTAINER (priv->list_box_search));
+
+ /* search for apps */
+ gs_plugin_loader_search_async (priv->plugin_loader,
+ value,
+ priv->cancellable,
+ gs_shell_search_get_search_cb,
+ shell_search);
+
+ spinner = GTK_SPINNER (gtk_builder_get_object (priv->builder, "spinner_search"));
+ gs_start_spinner (spinner);
+ priv->waiting = TRUE;
+}
+
+/**
+ * gs_shell_search_filter_text_changed_cb:
+ **/
+static void
+gs_shell_search_filter_text_changed_cb (GtkEntry *entry,
+ GsShellSearch *shell_search)
+{
+ /* FIXME: do something? */
+}
+
+/**
+ * gs_shell_search_sort_func:
+ **/
+static gint
+gs_shell_search_sort_func (GtkListBoxRow *a,
+ GtkListBoxRow *b,
+ gpointer user_data)
+{
+ GsAppWidget *aw1 = GS_APP_WIDGET (gtk_bin_get_child (GTK_BIN (a)));
+ GsAppWidget *aw2 = GS_APP_WIDGET (gtk_bin_get_child (GTK_BIN (b)));
+ GsApp *a1 = gs_app_widget_get_app (aw1);
+ GsApp *a2 = gs_app_widget_get_app (aw2);
+ guint64 date1 = gs_app_get_install_date (a1);
+ guint64 date2 = gs_app_get_install_date (a2);
+
+ if (date1 < date2)
+ return 1;
+ else if (date2 < date1)
+ return -1;
+
+ return g_strcmp0 (gs_app_get_name (a1),
+ gs_app_get_name (a2));
+}
+
+/**
+ * gs_shell_search_utf8_filter_helper:
+ **/
+static gboolean
+gs_shell_search_utf8_filter_helper (const gchar *haystack,
+ const gchar *needle_utf8)
+{
+ gboolean ret;
+ gchar *haystack_utf8;
+ haystack_utf8 = g_utf8_casefold (haystack, -1);
+ ret = strstr (haystack_utf8, needle_utf8) != NULL;
+ g_free (haystack_utf8);
+ return ret;
+}
+
+/**
+ * gs_shell_search_filter_func:
+ **/
+static gboolean
+gs_shell_search_filter_func (GtkListBoxRow *row, void *user_data)
+{
+ const gchar *tmp;
+ gboolean ret = TRUE;
+ gchar *needle_utf8 = NULL;
+ GsApp *app;
+ GsAppWidget *app_widget = GS_APP_WIDGET (gtk_bin_get_child (GTK_BIN (row)));
+ GsShellSearch *shell_search = GS_SHELL_SEARCH (user_data);
+ GsShellSearchPrivate *priv = shell_search->priv;
+ GtkWidget *widget;
+
+ app = gs_app_widget_get_app (app_widget);
+ widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "entry_search"));
+ tmp = gtk_entry_get_text (GTK_ENTRY (widget));
+ if (tmp[0] == '\0')
+ goto out;
+
+ needle_utf8 = g_utf8_casefold (tmp, -1);
+ ret = gs_shell_search_utf8_filter_helper (gs_app_get_name (app),
+ needle_utf8);
+ if (ret)
+ goto out;
+ ret = gs_shell_search_utf8_filter_helper (gs_app_get_summary (app),
+ needle_utf8);
+ if (ret)
+ goto out;
+ ret = gs_shell_search_utf8_filter_helper (gs_app_get_version (app),
+ needle_utf8);
+ if (ret)
+ goto out;
+ ret = gs_shell_search_utf8_filter_helper (gs_app_get_id (app),
+ needle_utf8);
+ if (ret)
+ goto out;
+out:
+ g_free (needle_utf8);
+ return ret;
+}
+
+/**
+ * gs_shell_search_list_header_func
+ **/
+static void
+gs_shell_search_list_header_func (GtkListBoxRow *row,
+ GtkListBoxRow *before,
+ gpointer user_data)
+{
+ GtkWidget *header;
+
+ /* first entry */
+ header = gtk_list_box_row_get_header (row);
+ if (before == NULL) {
+ gtk_list_box_row_set_header (row, NULL);
+ return;
+ }
+
+ /* already set */
+ if (header != NULL)
+ return;
+
+ /* set new */
+ header = gtk_separator_new (GTK_ORIENTATION_HORIZONTAL);
+ gtk_list_box_row_set_header (row, header);
+}
+
+/**
+ * gs_shell_search_pending_apps_changed_cb:
+ */
+static void
+gs_shell_search_pending_apps_changed_cb (GsPluginLoader *plugin_loader,
+ GsShellSearch *shell_search)
+{
+ gchar *label;
+ GPtrArray *pending;
+ GtkWidget *widget;
+
+ widget = GTK_WIDGET (gtk_builder_get_object (shell_search->priv->builder,
+ "label_button_search"));
+ pending = gs_plugin_loader_get_pending (plugin_loader);
+ if (pending->len == 0)
+ label = g_strdup (_("Search"));
+ else
+ label = g_strdup_printf (_("Search (%d)"), pending->len);
+ gtk_label_set_label (GTK_LABEL (widget), label);
+ g_free (label);
+ g_ptr_array_unref (pending);
+}
+
+/**
+ * gs_shell_search_setup:
+ */
+void
+gs_shell_search_setup (GsShellSearch *shell_search,
+ GsPluginLoader *plugin_loader,
+ GtkBuilder *builder,
+ GCancellable *cancellable)
+{
+ GsShellSearchPrivate *priv = shell_search->priv;
+ GtkWidget *widget;
+
+ g_return_if_fail (GS_IS_SHELL_SEARCH (shell_search));
+
+ priv->plugin_loader = g_object_ref (plugin_loader);
+ g_signal_connect (priv->plugin_loader, "pending-apps-changed",
+ G_CALLBACK (gs_shell_search_pending_apps_changed_cb),
+ shell_search);
+
+ priv->builder = g_object_ref (builder);
+ priv->cancellable = g_object_ref (cancellable);
+
+ /* refilter on search box changing */
+ widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "entry_search"));
+ g_signal_connect (GTK_EDITABLE (widget), "changed",
+ G_CALLBACK (gs_shell_search_filter_text_changed_cb), shell_search);
+
+ /* setup search */
+ priv->list_box_search = GTK_LIST_BOX (gtk_list_box_new ());
+ g_signal_connect (priv->list_box_search, "row-activated",
+ G_CALLBACK (gs_shell_search_app_widget_activated_cb), shell_search);
+ gtk_list_box_set_header_func (priv->list_box_search,
+ gs_shell_search_list_header_func,
+ shell_search,
+ NULL);
+ gtk_list_box_set_filter_func (priv->list_box_search,
+ gs_shell_search_filter_func,
+ shell_search,
+ NULL);
+ gtk_list_box_set_sort_func (priv->list_box_search,
+ gs_shell_search_sort_func,
+ shell_search,
+ NULL);
+ gtk_list_box_set_selection_mode (priv->list_box_search,
+ GTK_SELECTION_NONE);
+ widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "scrolledwindow_search"));
+ gtk_container_add (GTK_CONTAINER (widget), GTK_WIDGET (priv->list_box_search));
+ gtk_widget_show (GTK_WIDGET (priv->list_box_search));
+}
+
+/**
+ * gs_shell_search_class_init:
+ **/
+static void
+gs_shell_search_class_init (GsShellSearchClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ object_class->finalize = gs_shell_search_finalize;
+
+ g_type_class_add_private (klass, sizeof (GsShellSearchPrivate));
+}
+
+/**
+ * gs_shell_search_init:
+ **/
+static void
+gs_shell_search_init (GsShellSearch *shell_search)
+{
+ shell_search->priv = GS_SHELL_SEARCH_GET_PRIVATE (shell_search);
+ shell_search->priv->sizegroup_image = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
+ shell_search->priv->sizegroup_name = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
+}
+
+/**
+ * gs_shell_search_finalize:
+ **/
+static void
+gs_shell_search_finalize (GObject *object)
+{
+ GsShellSearch *shell_search = GS_SHELL_SEARCH (object);
+ GsShellSearchPrivate *priv = shell_search->priv;
+
+ g_object_unref (priv->builder);
+ g_object_unref (priv->plugin_loader);
+ g_object_unref (priv->cancellable);
+
+ G_OBJECT_CLASS (gs_shell_search_parent_class)->finalize (object);
+}
+
+/**
+ * gs_shell_search_new:
+ **/
+GsShellSearch *
+gs_shell_search_new (void)
+{
+ GsShellSearch *shell_search;
+ shell_search = g_object_new (GS_TYPE_SHELL_SEARCH, NULL);
+ return GS_SHELL_SEARCH (shell_search);
+}
diff --git a/src/gs-shell-search.h b/src/gs-shell-search.h
new file mode 100644
index 0000000..fcd8b17
--- /dev/null
+++ b/src/gs-shell-search.h
@@ -0,0 +1,62 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2013 Richard Hughes <richard hughsie com>
+ *
+ * Licensed under the GNU General Public License Version 2
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef __GS_SHELL_SEARCH_H
+#define __GS_SHELL_SEARCH_H
+
+#include <glib-object.h>
+#include <gtk/gtk.h>
+
+#include "gs-plugin-loader.h"
+
+G_BEGIN_DECLS
+
+#define GS_TYPE_SHELL_SEARCH (gs_shell_search_get_type ())
+#define GS_SHELL_SEARCH(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GS_TYPE_SHELL_SEARCH,
GsShellSearch))
+#define GS_SHELL_SEARCH_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GS_TYPE_SHELL_SEARCH,
GsShellSearchClass))
+#define GS_IS_SHELL_SEARCH(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GS_TYPE_SHELL_SEARCH))
+#define GS_IS_SHELL_SEARCH_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GS_TYPE_SHELL_SEARCH))
+#define GS_SHELL_SEARCH_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GS_TYPE_SHELL_SEARCH,
GsShellSearchClass))
+
+typedef struct GsShellSearchPrivate GsShellSearchPrivate;
+
+typedef struct
+{
+ GObject parent;
+ GsShellSearchPrivate *priv;
+} GsShellSearch;
+
+typedef struct
+{
+ GObjectClass parent_class;
+} GsShellSearchClass;
+
+GType gs_shell_search_get_type (void);
+
+GsShellSearch *gs_shell_search_new (void);
+void gs_shell_search_refresh (GsShellSearch *shell_search,
+ const gchar *text);
+void gs_shell_search_setup (GsShellSearch *shell_search,
+ GsPluginLoader *plugin_loader,
+ GtkBuilder *builder,
+ GCancellable *cancellable);
+
+#endif /* __GS_SHELL_SEARCH_H */
diff --git a/src/gs-shell-updates.c b/src/gs-shell-updates.c
index 9c46d69..b0608e6 100644
--- a/src/gs-shell-updates.c
+++ b/src/gs-shell-updates.c
@@ -170,6 +170,9 @@ gs_shell_updates_refresh (GsShellUpdates *shell_updates)
widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "scrolledwindow_updates"));
gtk_widget_show (widget);
+ widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "search_bar"));
+ gtk_widget_hide (widget);
+
if (priv->waiting)
return;
diff --git a/src/gs-shell.c b/src/gs-shell.c
index 3465c5b..331f190 100644
--- a/src/gs-shell.c
+++ b/src/gs-shell.c
@@ -27,6 +27,7 @@
#include "gs-shell.h"
#include "gs-shell-details.h"
#include "gs-shell-installed.h"
+#include "gs-shell-search.h"
#include "gs-shell-overview.h"
#include "gs-shell-updates.h"
#include "gs-shell-category.h"
@@ -43,6 +44,7 @@ struct GsShellPrivate
GsShellMode mode;
GsShellOverview *shell_overview;
GsShellInstalled *shell_installed;
+ GsShellSearch *shell_search;
GsShellUpdates *shell_updates;
GsShellDetails *shell_details;
GsShellCategory *shell_category;
@@ -73,6 +75,7 @@ gs_shell_set_overview_mode (GsShell *shell, GsShellMode mode, GsApp *app, const
{
GsShellPrivate *priv = shell->priv;
GtkWidget *widget;
+ const gchar *text;
if (priv->ignore_primary_buttons)
return;
@@ -92,6 +95,8 @@ gs_shell_set_overview_mode (GsShell *shell, GsShellMode mode, GsApp *app, const
gtk_widget_hide (widget);
widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "buttonbox_main"));
gtk_widget_hide (widget);
+ widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "search_bar"));
+ gtk_widget_hide (widget);
/* update main buttons according to mode */
priv->ignore_primary_buttons = TRUE;
@@ -118,6 +123,11 @@ gs_shell_set_overview_mode (GsShell *shell, GsShellMode mode, GsApp *app, const
case GS_SHELL_MODE_INSTALLED:
gs_shell_installed_refresh (priv->shell_installed);
break;
+ case GS_SHELL_MODE_SEARCH:
+ widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "entry_search"));
+ text = gtk_entry_get_text (GTK_ENTRY (widget));
+ gs_shell_search_refresh (priv->shell_search, text);
+ break;
case GS_SHELL_MODE_UPDATES:
gs_shell_updates_refresh (priv->shell_updates);
break;
@@ -222,6 +232,10 @@ gs_shell_setup (GsShell *shell, GsPluginLoader *plugin_loader, GCancellable *can
priv->plugin_loader,
priv->builder,
priv->cancellable);
+ gs_shell_search_setup (priv->shell_search,
+ priv->plugin_loader,
+ priv->builder,
+ priv->cancellable);
gs_shell_details_setup (priv->shell_details,
shell,
priv->plugin_loader,
@@ -290,6 +304,7 @@ gs_shell_init (GsShell *shell)
shell->priv->shell_installed = gs_shell_installed_new ();
shell->priv->shell_details = gs_shell_details_new ();
shell->priv->shell_category = gs_shell_category_new ();
+ shell->priv->shell_search = gs_shell_search_new ();
shell->priv->ignore_primary_buttons = FALSE;
}
@@ -310,6 +325,7 @@ gs_shell_finalize (GObject *object)
g_object_unref (priv->shell_updates);
g_object_unref (priv->shell_details);
g_object_unref (priv->shell_category);
+ g_object_unref (priv->shell_search);
G_OBJECT_CLASS (gs_shell_parent_class)->finalize (object);
}
diff --git a/src/gs-shell.h b/src/gs-shell.h
index f31c6b7..4a22915 100644
--- a/src/gs-shell.h
+++ b/src/gs-shell.h
@@ -52,6 +52,7 @@ typedef struct
typedef enum {
GS_SHELL_MODE_OVERVIEW,
GS_SHELL_MODE_INSTALLED,
+ GS_SHELL_MODE_SEARCH,
GS_SHELL_MODE_UPDATES,
GS_SHELL_MODE_DETAILS,
GS_SHELL_MODE_CATEGORY
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]