[epiphany] URL entry: Replacing GtkEntry with DzlSuggestionEntry
- From: Michael Catanzaro <mcatanzaro src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [epiphany] URL entry: Replacing GtkEntry with DzlSuggestionEntry
- Date: Sun, 16 Sep 2018 14:43:18 +0000 (UTC)
commit fccdc31d7b02dcb0c8c2f9aca86179694ab071dd
Author: Jan-Michael Brummer <jan brummer tabos org>
Date: Sat Jun 23 23:51:02 2018 +0200
URL entry: Replacing GtkEntry with DzlSuggestionEntry
Make use of dazzles suggestion entry widget to make url entry more fancy.
embed/meson.build | 2 +-
lib/ephy-suggestion.c | 194 +++++++++
lib/ephy-suggestion.h | 39 ++
lib/meson.build | 2 +
lib/widgets/contrib/gd-two-lines-renderer.c | 619 ---------------------------
lib/widgets/contrib/gd-two-lines-renderer.h | 36 --
lib/widgets/ephy-location-entry.c | 375 ++---------------
lib/widgets/ephy-location-entry.h | 17 +-
lib/widgets/meson.build | 4 +-
meson.build | 2 +-
src/ephy-completion-model.c | 625 ----------------------------
src/ephy-completion-model.h | 53 ---
src/ephy-location-controller.c | 135 +-----
src/ephy-suggestion-model.c | 521 +++++++++++++++++++++++
src/ephy-suggestion-model.h | 46 ++
src/ephy-window.c | 2 +-
src/meson.build | 2 +-
src/search-provider/ephy-search-provider.c | 119 ++----
subprojects/libdazzle.wrap | 2 +-
tests/ephy-completion-model-test.c | 101 -----
tests/ephy-location-entry-test.c | 21 +-
tests/meson.build | 9 -
22 files changed, 910 insertions(+), 2016 deletions(-)
---
diff --git a/embed/meson.build b/embed/meson.build
index df5edd6e5..e3a217c34 100644
--- a/embed/meson.build
+++ b/embed/meson.build
@@ -32,7 +32,7 @@ libephyembed_sources = [
]
libephyembed_deps = [
- dazzle_dep,
+ libdazzle_dep,
ephymisc_dep,
gio_dep,
glib_dep,
diff --git a/lib/ephy-suggestion.c b/lib/ephy-suggestion.c
new file mode 100644
index 000000000..9c7beec36
--- /dev/null
+++ b/lib/ephy-suggestion.c
@@ -0,0 +1,194 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * Copyright © 2017 Igalia S.L.
+ *
+ * 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 "config.h"
+#include "ephy-suggestion.h"
+
+#include "ephy-uri-helpers.h"
+
+#include <dazzle.h>
+#include <glib.h>
+
+struct _EphySuggestion {
+ DzlSuggestion parent;
+
+ char *unescaped_title;
+ cairo_surface_t *favicon;
+};
+
+G_DEFINE_TYPE (EphySuggestion, ephy_suggestion, DZL_TYPE_SUGGESTION)
+
+enum {
+ PROP_0,
+ PROP_UNESCAPED_TITLE,
+ LAST_PROP
+};
+
+static GParamSpec *obj_properties[LAST_PROP];
+
+static void
+ephy_suggestion_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ EphySuggestion *self = EPHY_SUGGESTION (object);
+
+ switch (prop_id) {
+ case PROP_UNESCAPED_TITLE:
+ self->unescaped_title = g_strdup (g_value_get_string (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+ephy_suggestion_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ EphySuggestion *self = EPHY_SUGGESTION (object);
+
+ switch (prop_id) {
+ case PROP_UNESCAPED_TITLE:
+ g_value_set_string (value, self->unescaped_title);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+char *
+ephy_suggestion_replace_typed_text (DzlSuggestion *self,
+ const char *typed_text)
+{
+ const char *url;
+
+ g_assert (EPHY_IS_SUGGESTION (self));
+
+ url = ephy_suggestion_get_uri (EPHY_SUGGESTION (self));
+
+ return g_strdup (url);
+}
+
+cairo_surface_t *
+ephy_suggestion_get_icon_surface (DzlSuggestion *self,
+ GtkWidget *widget)
+{
+ EphySuggestion *suggestion = EPHY_SUGGESTION (self);
+
+ return suggestion->favicon;
+}
+
+
+static void
+ephy_suggestion_class_init (EphySuggestionClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ DzlSuggestionClass *dzl_suggestion_class = DZL_SUGGESTION_CLASS (klass);
+
+ object_class->get_property = ephy_suggestion_get_property;
+ object_class->set_property = ephy_suggestion_set_property;
+
+ dzl_suggestion_class->replace_typed_text = ephy_suggestion_replace_typed_text;
+ dzl_suggestion_class->get_icon_surface = ephy_suggestion_get_icon_surface;
+
+ obj_properties[PROP_UNESCAPED_TITLE] =
+ g_param_spec_string ("unescaped-title",
+ "Unescaped title",
+ "The title of the suggestion, not XML-escaped",
+ "",
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
+
+ g_object_class_install_properties (object_class, LAST_PROP, obj_properties);
+}
+
+static void
+ephy_suggestion_init (EphySuggestion *self)
+{
+}
+
+EphySuggestion *
+ephy_suggestion_new (const char *title,
+ const char *uri)
+{
+ EphySuggestion *suggestion;
+ char *escaped_title = g_markup_escape_text (title, -1);
+ char *decoded_uri = ephy_uri_decode (uri);
+ char *escaped_uri = g_markup_escape_text (decoded_uri, -1);
+
+ suggestion = g_object_new (EPHY_TYPE_SUGGESTION,
+ "icon-name", "web-browser-symbolic",
+ "id", uri,
+ "subtitle", escaped_uri,
+ "title", escaped_title,
+ "unescaped-title", title,
+ NULL);
+
+ g_free (escaped_title);
+ g_free (decoded_uri);
+ g_free (escaped_uri);
+
+ return suggestion;
+}
+
+EphySuggestion *
+ephy_suggestion_new_without_subtitle (const char *title,
+ const char *uri)
+{
+ EphySuggestion *suggestion;
+ char *escaped_title;
+
+ escaped_title = g_markup_escape_text (title, -1);
+ suggestion = g_object_new (EPHY_TYPE_SUGGESTION,
+ "icon-name", "web-browser-symbolic",
+ "id", uri,
+ "title", escaped_title,
+ "unescaped-title", title,
+ NULL);
+
+ g_free (escaped_title);
+
+ return suggestion;
+}
+
+const char *
+ephy_suggestion_get_unescaped_title (EphySuggestion *self)
+{
+ g_assert (EPHY_IS_SUGGESTION (self));
+
+ return self->unescaped_title;
+}
+
+const char *
+ephy_suggestion_get_uri (EphySuggestion *self)
+{
+ g_assert (EPHY_IS_SUGGESTION (self));
+
+ return dzl_suggestion_get_id (DZL_SUGGESTION (self));
+}
+
+void
+ephy_suggestion_set_favicon (EphySuggestion *self,
+ cairo_surface_t *favicon)
+{
+ self->favicon = favicon;
+ g_object_notify (G_OBJECT (self), "icon");
+}
diff --git a/lib/ephy-suggestion.h b/lib/ephy-suggestion.h
new file mode 100644
index 000000000..ca461120b
--- /dev/null
+++ b/lib/ephy-suggestion.h
@@ -0,0 +1,39 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * Copyright © 2017 Igalia S.L.
+ *
+ * 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/>.
+ */
+
+#pragma once
+
+#include <dazzle.h>
+
+G_BEGIN_DECLS
+
+#define EPHY_TYPE_SUGGESTION (ephy_suggestion_get_type())
+
+G_DECLARE_FINAL_TYPE (EphySuggestion, ephy_suggestion, EPHY, SUGGESTION, DzlSuggestion)
+
+EphySuggestion *ephy_suggestion_new (const char *title,
+ const char *uri);
+EphySuggestion *ephy_suggestion_new_without_subtitle (const char *title,
+ const char *uri);
+const char *ephy_suggestion_get_unescaped_title (EphySuggestion *self);
+const char *ephy_suggestion_get_uri (EphySuggestion *self);
+
+void ephy_suggestion_set_favicon (EphySuggestion *self,
+ cairo_surface_t *favicon);
+
+G_END_DECLS
diff --git a/lib/meson.build b/lib/meson.build
index 5b2affa49..8196d1f68 100644
--- a/lib/meson.build
+++ b/lib/meson.build
@@ -34,6 +34,7 @@ libephymisc_sources = [
'ephy-sqlite-connection.c',
'ephy-sqlite-statement.c',
'ephy-string.c',
+ 'ephy-suggestion.c',
'ephy-sync-utils.c',
'ephy-time-helpers.c',
'ephy-uri-helpers.c',
@@ -61,6 +62,7 @@ libephymisc_deps = [
gtk_dep,
icu_uc_dep,
json_glib_dep,
+ libdazzle_dep,
libsecret_dep,
libsoup_dep,
libxml_dep,
diff --git a/lib/widgets/ephy-location-entry.c b/lib/widgets/ephy-location-entry.c
index b86ff1524..5e6c7de4e 100644
--- a/lib/widgets/ephy-location-entry.c
+++ b/lib/widgets/ephy-location-entry.c
@@ -31,10 +31,11 @@
#include "ephy-gui.h"
#include "ephy-lib-type-builtins.h"
#include "ephy-signal-accumulator.h"
+#include "ephy-suggestion.h"
#include "ephy-title-widget.h"
#include "ephy-uri-helpers.h"
-#include "gd-two-lines-renderer.h"
+#include <dazzle.h>
#include <gdk/gdkkeysyms.h>
#include <glib/gi18n.h>
#include <gtk/gtk.h>
@@ -103,12 +104,6 @@ struct _EphyLocationEntry {
static gboolean ephy_location_entry_reset_internal (EphyLocationEntry *, gboolean);
-static void extracell_data_func (GtkCellLayout *cell_layout,
- GtkCellRenderer *cell,
- GtkTreeModel *tree_model,
- GtkTreeIter *iter,
- gpointer data);
-
enum {
PROP_0,
PROP_ADDRESS,
@@ -131,6 +126,12 @@ G_DEFINE_TYPE_WITH_CODE (EphyLocationEntry, ephy_location_entry, GTK_TYPE_OVERLA
G_IMPLEMENT_INTERFACE (EPHY_TYPE_TITLE_WIDGET,
ephy_location_entry_title_widget_interface_init))
+static void
+ephy_location_entry_activate (EphyLocationEntry *entry)
+{
+ g_signal_emit_by_name (entry->url_entry, "activate");
+}
+
static void
update_address_state (EphyLocationEntry *entry)
{
@@ -523,99 +524,14 @@ entry_key_press_cb (GtkEntry *entry,
ephy_location_entry_activate (location_entry);
}
- return FALSE;
-}
-
-static gboolean
-entry_key_press_after_cb (GtkEntry *entry,
- GdkEventKey *event,
- EphyLocationEntry *lentry)
-{
- guint state = event->state & gtk_accelerator_get_default_mod_mask ();
-
- if ((event->keyval == GDK_KEY_Return ||
- event->keyval == GDK_KEY_KP_Enter ||
- event->keyval == GDK_KEY_ISO_Enter) &&
- (state == GDK_CONTROL_MASK ||
- state == (GDK_CONTROL_MASK | GDK_SHIFT_MASK))) {
- /* gtk_im_context_reset (entry->im_context); */
-
- lentry->needs_reset = TRUE;
- g_signal_emit_by_name (entry, "activate");
-
- return TRUE;
- }
-
- if ((event->keyval == GDK_KEY_Down || event->keyval == GDK_KEY_KP_Down)
- && state == 0) {
- /* If we are focusing the entry, with the cursor at the end of it
- * we emit the changed signal, so that the completion popup appears */
- const char *string;
-
- string = gtk_entry_get_text (entry);
- if (gtk_editable_get_position (GTK_EDITABLE (entry)) == (int)strlen (string)) {
- g_signal_emit_by_name (entry, "changed", 0);
- return TRUE;
- }
- }
+ if (event->keyval == GDK_KEY_Return ||
+ event->keyval == GDK_KEY_KP_Enter ||
+ event->keyval == GDK_KEY_ISO_Enter)
+ ephy_location_entry_activate (location_entry);
return FALSE;
}
-static void
-entry_activate_after_cb (GtkEntry *entry,
- EphyLocationEntry *lentry)
-{
- lentry->user_changed = FALSE;
-
- if (lentry->needs_reset) {
- ephy_location_entry_reset_internal (lentry, TRUE);
- lentry->needs_reset = FALSE;
- }
-}
-
-static gboolean
-match_selected_cb (GtkEntryCompletion *completion,
- GtkTreeModel *model,
- GtkTreeIter *iter,
- EphyLocationEntry *entry)
-{
- char *item = NULL;
- guint state;
-
- gtk_tree_model_get (model, iter,
- entry->action_col, &item, -1);
- if (item == NULL) return FALSE;
-
- ephy_gui_get_current_event (NULL, &state, NULL);
-
- entry->needs_reset = (state == GDK_CONTROL_MASK ||
- state == (GDK_CONTROL_MASK | GDK_SHIFT_MASK));
-
- ephy_title_widget_set_address (EPHY_TITLE_WIDGET (entry), item);
- /* gtk_im_context_reset (GTK_ENTRY (entry)->im_context); */
- g_signal_emit_by_name (entry->url_entry, "activate");
-
- g_free (item);
-
- return TRUE;
-}
-
-static void
-action_activated_after_cb (GtkEntryCompletion *completion,
- gint index,
- EphyLocationEntry *lentry)
-{
- guint state, button;
-
- ephy_gui_get_current_event (NULL, &state, &button);
- if ((state == GDK_CONTROL_MASK ||
- state == (GDK_CONTROL_MASK | GDK_SHIFT_MASK)) ||
- button == 2) {
- ephy_location_entry_reset_internal (lentry, TRUE);
- }
-}
-
static void
entry_clear_activate_cb (GtkMenuItem *item,
EphyLocationEntry *entry)
@@ -633,7 +549,7 @@ paste_received (GtkClipboard *clipboard,
{
if (text) {
gtk_entry_set_text (GTK_ENTRY (entry->url_entry), text);
- g_signal_emit_by_name (entry->url_entry, "activate");
+ ephy_location_entry_activate (entry);
}
}
@@ -814,6 +730,19 @@ button_box_size_allocated_cb (GtkWidget *widget,
g_free (css);
}
+static void
+ephy_location_entry_suggestion_activated (DzlSuggestionEntry *entry,
+ DzlSuggestion *arg1,
+ gpointer user_data)
+{
+ EphyLocationEntry *lentry = EPHY_LOCATION_ENTRY (user_data);
+ DzlSuggestion *suggestion = dzl_suggestion_entry_get_suggestion (entry);
+ gtk_entry_set_text (GTK_ENTRY (entry), ephy_suggestion_get_uri (EPHY_SUGGESTION (suggestion)));
+
+ /* Now trigger the load.... */
+ ephy_location_entry_activate (EPHY_LOCATION_ENTRY (lentry));
+}
+
static void
ephy_location_entry_construct_contents (EphyLocationEntry *entry)
{
@@ -823,18 +752,18 @@ ephy_location_entry_construct_contents (EphyLocationEntry *entry)
LOG ("EphyLocationEntry constructing contents %p", entry);
/* URL entry */
- entry->url_entry = gtk_entry_new ();
+ entry->url_entry = dzl_suggestion_entry_new ();
/* Add special widget css provider */
context = gtk_widget_get_style_context (GTK_WIDGET (entry->url_entry));
entry->css_provider = gtk_css_provider_new ();
gtk_style_context_add_provider (context, GTK_STYLE_PROVIDER (entry->css_provider),
GTK_STYLE_PROVIDER_PRIORITY_USER);
- gtk_style_context_add_class (gtk_widget_get_style_context (entry->url_entry), "url_entry");
+ gtk_style_context_add_class (gtk_widget_get_style_context (GTK_WIDGET (entry->url_entry)), "url_entry");
g_signal_connect (G_OBJECT (entry->url_entry), "copy-clipboard", G_CALLBACK
(ephy_location_entry_copy_clipboard), NULL);
g_signal_connect (G_OBJECT (entry->url_entry), "cut-clipboard", G_CALLBACK
(ephy_location_entry_cut_clipboard), NULL);
- gtk_widget_show (entry->url_entry);
- gtk_overlay_add_overlay (GTK_OVERLAY (entry), entry->url_entry);
+ gtk_widget_show (GTK_WIDGET (entry->url_entry));
+ gtk_overlay_add_overlay (GTK_OVERLAY (entry), GTK_WIDGET (entry->url_entry));
/* Button Box */
button_box = gtk_button_box_new (GTK_ORIENTATION_HORIZONTAL);
@@ -874,10 +803,8 @@ ephy_location_entry_construct_contents (EphyLocationEntry *entry)
"signal::changed", G_CALLBACK (editable_changed_cb), entry,
NULL);
- g_signal_connect_after (entry->url_entry, "key-press-event",
- G_CALLBACK (entry_key_press_after_cb), entry);
- g_signal_connect_after (entry->url_entry, "activate",
- G_CALLBACK (entry_activate_after_cb), entry);
+ g_signal_connect (entry->url_entry, "suggestion-activated",
+ G_CALLBACK (ephy_location_entry_suggestion_activated), entry);
}
static void
@@ -954,238 +881,6 @@ schedule_dns_prefetch (EphyLocationEntry *entry, guint interval, const gchar *ur
}
#endif
-static gboolean
-cursor_on_match_cb (GtkEntryCompletion *completion,
- GtkTreeModel *model,
- GtkTreeIter *iter,
- EphyLocationEntry *le)
-{
- char *url = NULL;
- GtkWidget *entry;
-
- gtk_tree_model_get (model, iter,
- le->url_col,
- &url, -1);
- entry = gtk_entry_completion_get_entry (completion);
-
- /* Prevent the update so we keep the highlight from our input.
- * See textcell_data_func().
- */
- le->block_update = TRUE;
- gtk_entry_set_text (GTK_ENTRY (entry), url);
- gtk_editable_set_position (GTK_EDITABLE (entry), -1);
- le->block_update = FALSE;
-
-#if 0
-/* FIXME: Refactor the DNS prefetch, this is a layering violation */
- schedule_dns_prefetch (le, 250, (const gchar *)url);
-#endif
-
- g_free (url);
-
- return TRUE;
-}
-
-static void
-extracell_data_func (GtkCellLayout *cell_layout,
- GtkCellRenderer *cell,
- GtkTreeModel *tree_model,
- GtkTreeIter *iter,
- gpointer data)
-{
- EphyLocationEntry *entry = EPHY_LOCATION_ENTRY (data);
- gboolean is_bookmark = FALSE;
- GValue visible = { 0, };
-
- gtk_tree_model_get (tree_model, iter,
- entry->extra_col, &is_bookmark,
- -1);
-
- if (is_bookmark)
- g_object_set (cell,
- "icon-name", "starred-symbolic",
- NULL);
-
- g_value_init (&visible, G_TYPE_BOOLEAN);
- g_value_set_boolean (&visible, is_bookmark);
- g_object_set_property (G_OBJECT (cell), "visible", &visible);
- g_value_unset (&visible);
-}
-
-/**
- * ephy_location_entry_set_match_func:
- * @entry: an #EphyLocationEntry widget
- * @match_func: a #GtkEntryCompletionMatchFunc
- * @user_data: user_data to pass to the @match_func
- * @notify: a #GDestroyNotify, like the one given to
- * gtk_entry_completion_set_match_func
- *
- * Sets the match_func for the internal #GtkEntryCompletion to @match_func.
- *
- **/
-void
-ephy_location_entry_set_match_func (EphyLocationEntry *entry,
- GtkEntryCompletionMatchFunc match_func,
- gpointer user_data,
- GDestroyNotify notify)
-{
- GtkEntryCompletion *completion;
-
- completion = gtk_entry_get_completion (GTK_ENTRY (entry->url_entry));
- gtk_entry_completion_set_match_func (completion, match_func, user_data, notify);
-}
-
-/**
- * ephy_location_entry_set_completion:
- * @entry: an #EphyLocationEntry widget
- * @model: the #GtkModel for the completion
- * @text_col: column id to access #GtkModel relevant data
- * @action_col: column id to access #GtkModel relevant data
- * @keywords_col: column id to access #GtkModel relevant data
- * @relevance_col: column id to access #GtkModel relevant data
- * @url_col: column id to access #GtkModel relevant data
- * @extra_col: column id to access #GtkModel relevant data
- * @favicon_col: column id to access #GtkModel relevant data
- *
- * Initializes @entry to have a #GtkEntryCompletion using @model as the
- * internal #GtkModel. The *_col arguments are for internal data retrieval from
- * @model, like when setting the text property of one of the #GtkCellRenderer
- * of the completion.
- *
- **/
-void
-ephy_location_entry_set_completion (EphyLocationEntry *entry,
- GtkTreeModel *model,
- guint text_col,
- guint action_col,
- guint keywords_col,
- guint relevance_col,
- guint url_col,
- guint extra_col,
- guint favicon_col)
-{
- GtkEntryCompletion *completion;
- GtkCellRenderer *cell;
-
- entry->text_col = text_col;
- entry->action_col = action_col;
- entry->keywords_col = keywords_col;
- entry->relevance_col = relevance_col;
- entry->url_col = url_col;
- entry->extra_col = extra_col;
- entry->favicon_col = favicon_col;
-
- completion = gtk_entry_completion_new ();
- gtk_entry_completion_set_model (completion, model);
- g_signal_connect (completion, "match-selected",
- G_CALLBACK (match_selected_cb), entry);
- g_signal_connect_after (completion, "action-activated",
- G_CALLBACK (action_activated_after_cb), entry);
-
- cell = gtk_cell_renderer_pixbuf_new ();
- gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (completion),
- cell, FALSE);
- gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (completion),
- cell, "pixbuf", favicon_col);
-
- /* Pixel-perfect aligment with the location entry favicon
- * (16x16). Consider that this /might/ depend on the theme.
- *
- * The GtkEntryCompletion can not be themed so we work-around
- * that with padding and fixed sizes.
- * For the first cell, this is:
- *
- * ___+++++iiiiiiiiiiiiiiii++__ttt...bbb++++++__
- *
- * _ = widget spacing, can not be handled (3 px)
- * + = padding (5 px) (ICON_PADDING_LEFT)
- * i = the icon (16 px) (ICON_CONTENT_WIDTH)
- * + = padding (2 px) (ICON_PADDING_RIGHT) (cut by the fixed_size)
- * _ = spacing between cells, can not be handled (2 px)
- * t = the text (expands)
- * b = bookmark icon (16 px)
- * + = padding (6 px) (BKMK_PADDING_RIGHT)
- * _ = widget spacing, can not be handled (2 px)
- *
- * Each character is a pixel.
- *
- * The text cell and the bookmark icon cell are much more
- * flexible in its aligment, because they do not have to align
- * with anything in the entry.
- */
-
-#define ROW_PADDING_VERT 4
-
-#define ICON_PADDING_LEFT 5
-#define ICON_CONTENT_WIDTH 16
-#define ICON_PADDING_RIGHT 9
-
-#define ICON_CONTENT_HEIGHT 16
-
-#define TEXT_PADDING_LEFT 0
-
-#define BKMK_PADDING_RIGHT 6
-
- gtk_cell_renderer_set_padding
- (cell, ICON_PADDING_LEFT, ROW_PADDING_VERT);
- gtk_cell_renderer_set_fixed_size
- (cell,
- (ICON_PADDING_LEFT + ICON_CONTENT_WIDTH + ICON_PADDING_RIGHT),
- ICON_CONTENT_HEIGHT);
- gtk_cell_renderer_set_alignment (cell, 0.0, 0.5);
-
- cell = gd_two_lines_renderer_new ();
- g_object_set (cell,
- "ellipsize", PANGO_ELLIPSIZE_END,
- "text-lines", 2,
- NULL);
- gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (completion),
- cell, TRUE);
- gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (completion),
- cell, "text", text_col);
- gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (completion),
- cell, "line-two", url_col);
-
- /* Pixel-perfect aligment with the text in the location entry.
- * See above.
- */
- gtk_cell_renderer_set_padding
- (cell, TEXT_PADDING_LEFT, ROW_PADDING_VERT);
- gtk_cell_renderer_set_alignment (cell, 0.0, 0.5);
-
- /*
- * As the width of the entry completion is known in advance
- * (as big as the entry you are completing on), we can set
- * any fixed width (the 1 is just this random number here)
- * Since the height is known too, we avoid computing the actual
- * sizes of the cells, which takes a lot of CPU time and does
- * not get used anyway.
- */
- gtk_cell_renderer_set_fixed_size (cell, 1, -1);
- gtk_cell_renderer_text_set_fixed_height_from_font (GTK_CELL_RENDERER_TEXT (cell), 2);
-
- cell = gtk_cell_renderer_pixbuf_new ();
- g_object_set (cell, "follow-state", TRUE, NULL);
- gtk_cell_layout_pack_end (GTK_CELL_LAYOUT (completion),
- cell, FALSE);
- gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (completion),
- cell, extracell_data_func,
- entry,
- NULL);
-
- /* Pixel-perfect aligment. This just keeps the same margin from
- * the border than the favicon on the other side. See above. */
- gtk_cell_renderer_set_padding
- (cell, BKMK_PADDING_RIGHT, ROW_PADDING_VERT);
-
- g_object_set (completion, "inline-selection", TRUE, NULL);
- g_signal_connect (completion, "cursor-on-match",
- G_CALLBACK (cursor_on_match_cb), entry);
-
- gtk_entry_set_completion (GTK_ENTRY (entry->url_entry), completion);
- g_object_unref (completion);
-}
-
/**
* ephy_location_entry_get_can_undo:
* @entry: an #EphyLocationEntry widget
@@ -1282,7 +977,7 @@ ephy_location_entry_reset (EphyLocationEntry *entry)
}
/**
- * ephy_location_entry_activate:
+ * ephy_location_entry_focus:
* @entry: an #EphyLocationEntry widget
*
* Set focus on @entry and select the text whithin. This is called when the
@@ -1290,7 +985,7 @@ ephy_location_entry_reset (EphyLocationEntry *entry)
*
**/
void
-ephy_location_entry_activate (EphyLocationEntry *entry)
+ephy_location_entry_focus (EphyLocationEntry *entry)
{
GtkWidget *toplevel, *widget = GTK_WIDGET (entry->url_entry);
@@ -1391,7 +1086,7 @@ ephy_location_entry_get_search_terms (EphyLocationEntry *entry)
GtkWidget *
ephy_location_entry_get_entry (EphyLocationEntry *entry)
{
- return entry->url_entry;
+ return GTK_WIDGET (entry->url_entry);
}
GtkWidget *
diff --git a/lib/widgets/ephy-location-entry.h b/lib/widgets/ephy-location-entry.h
index 7056df801..65960ebea 100644
--- a/lib/widgets/ephy-location-entry.h
+++ b/lib/widgets/ephy-location-entry.h
@@ -41,21 +41,6 @@ typedef enum {
GtkWidget *ephy_location_entry_new (void);
-void ephy_location_entry_set_completion (EphyLocationEntry *entry,
- GtkTreeModel *model,
- guint text_col,
- guint action_col,
- guint keywords_col,
- guint relevance_col,
- guint url_col,
- guint extra_col,
- guint favicon_col);
-
-void ephy_location_entry_set_match_func (EphyLocationEntry *entry,
- GtkEntryCompletionMatchFunc match_func,
- gpointer user_data,
- GDestroyNotify notify);
-
gboolean ephy_location_entry_get_can_undo (EphyLocationEntry *entry);
gboolean ephy_location_entry_get_can_redo (EphyLocationEntry *entry);
@@ -66,7 +51,7 @@ gboolean ephy_location_entry_reset (EphyLocationEntr
void ephy_location_entry_undo_reset (EphyLocationEntry *entry);
-void ephy_location_entry_activate (EphyLocationEntry *entry);
+void ephy_location_entry_focus (EphyLocationEntry *entry);
void ephy_location_entry_set_bookmark_icon_state (EphyLocationEntry *entry,
EphyLocationEntryBookmarkIconState state);
diff --git a/lib/widgets/meson.build b/lib/widgets/meson.build
index afdb75293..cea7788a7 100644
--- a/lib/widgets/meson.build
+++ b/lib/widgets/meson.build
@@ -7,7 +7,6 @@ enums = gnome.mkenums_simple('ephy-widgets-type-builtins',
)
libephywidgets_sources = [
- 'contrib/gd-two-lines-renderer.c',
'contrib/nautilus-floating-bar.c',
'ephy-certificate-dialog.c',
'ephy-downloads-popover.c',
@@ -23,7 +22,7 @@ libephywidgets_sources = [
]
libephywidgets_deps = [
- dazzle_dep,
+ libdazzle_dep,
ephymisc_dep,
gcr_dep,
gdk_dep,
@@ -31,6 +30,7 @@ libephywidgets_deps = [
gio_dep,
glib_dep,
gtk_dep,
+ libdazzle_dep,
libsoup_dep,
webkit2gtk_dep,
]
diff --git a/meson.build b/meson.build
index 8ba6782dd..7e1ed02a3 100644
--- a/meson.build
+++ b/meson.build
@@ -71,7 +71,6 @@ nettle_requirement = '>= 3.2'
webkitgtk_requirement = '>= 2.21.92'
cairo_dep = dependency('cairo', version: '>= 1.2')
-dazzle_dep = dependency('libdazzle-1.0', version: '>= 3.29.91', fallback : ['libdazzle', 'libdazzle_dep'])
gcr_dep = dependency('gcr-3', version: '>= 3.5.5')
gdk_dep = dependency('gdk-3.0', version: gtk_requirement)
gdk_pixbuf_dep = dependency('gdk-pixbuf-2.0', version: '>= 2.36.5')
@@ -84,6 +83,7 @@ hogweed_dep = dependency('hogweed', version: nettle_requirement)
icu_uc_dep = dependency('icu-uc', version: '>= 4.6')
iso_codes_dep = dependency('iso-codes', version: '>= 0.35')
json_glib_dep = dependency('json-glib-1.0', version: '>= 1.2.4')
+libdazzle_dep = dependency('libdazzle-1.0', version: '>= 3.29.4', fallback : ['libdazzle', 'libdazzle_dep'])
libnotify_dep = dependency('libnotify', version: '>= 0.5.1')
libsecret_dep = dependency('libsecret-1', version: '>= 0.14')
libsoup_dep = dependency('libsoup-2.4', version: '>= 2.48.0')
diff --git a/src/ephy-location-controller.c b/src/ephy-location-controller.c
index cb9440097..a3d4cb18f 100644
--- a/src/ephy-location-controller.c
+++ b/src/ephy-location-controller.c
@@ -22,8 +22,6 @@
#include "config.h"
#include "ephy-location-controller.h"
-
-#include "ephy-completion-model.h"
#include "ephy-debug.h"
#include "ephy-dnd.h"
#include "ephy-embed-container.h"
@@ -31,10 +29,12 @@
#include "ephy-link.h"
#include "ephy-location-entry.h"
#include "ephy-shell.h"
+#include "ephy-suggestion-model.h"
#include "ephy-title-widget.h"
#include "ephy-uri-helpers.h"
#include "ephy-widgets-type-builtins.h"
+#include <dazzle.h>
#include <gdk/gdkkeysyms.h>
#include <gtk/gtk.h>
#include <string.h>
@@ -80,16 +80,6 @@ G_DEFINE_TYPE_WITH_CODE (EphyLocationController, ephy_location_controller, G_TYP
G_IMPLEMENT_INTERFACE (EPHY_TYPE_LINK,
NULL))
-static gboolean
-match_func (GtkEntryCompletion *completion,
- const char *key,
- GtkTreeIter *iter,
- gpointer data)
-{
- /* We want every row in the model to show up. */
- return TRUE;
-}
-
static void
entry_drag_data_received_cb (GtkWidget *widget,
GdkDragContext *context,
@@ -182,36 +172,22 @@ entry_activate_cb (GtkEntry *entry,
g_free (effective_address);
}
-static void
-update_done_cb (EphyHistoryService *service,
- gboolean success,
- gpointer result_data,
- gpointer user_data)
-{
- /* FIXME: this hack is needed for the completion entry popup
- * to resize smoothly. See:
- * https://bugzilla.gnome.org/show_bug.cgi?id=671074 */
- gtk_entry_completion_complete (GTK_ENTRY_COMPLETION (user_data));
-}
static void
user_changed_cb (GtkWidget *widget, EphyLocationController *controller)
{
const char *address;
- GtkTreeModel *model;
- GtkEntryCompletion *completion;
GtkWidget *entry;
+ GListModel *model;
address = ephy_title_widget_get_address (EPHY_TITLE_WIDGET (widget));
LOG ("user_changed_cb, address %s", address);
entry = ephy_location_entry_get_entry (EPHY_LOCATION_ENTRY (widget));
- completion = gtk_entry_get_completion (GTK_ENTRY (entry));
- model = gtk_entry_completion_get_model (completion);
+ model = dzl_suggestion_entry_get_model (DZL_SUGGESTION_ENTRY (entry));
- ephy_completion_model_update_for_string (EPHY_COMPLETION_MODEL (model), address,
- update_done_cb, completion);
+ ephy_suggestion_model_query_async (EPHY_SUGGESTION_MODEL (model), address, NULL, NULL, NULL);
}
static void
@@ -294,83 +270,6 @@ switch_page_cb (GtkNotebook *notebook,
}
}
-static void
-action_activated_cb (GtkEntryCompletion *completion,
- int index,
- EphyLocationController *controller)
-{
- GtkWidget *entry;
- char *url = NULL;
- char *content;
- char **engine_names;
-
- entry = gtk_entry_completion_get_entry (completion);
- content = gtk_editable_get_chars (GTK_EDITABLE (entry), 0, -1);
- if (content == NULL)
- return;
-
- engine_names = ephy_search_engine_manager_get_names (controller->search_engine_manager);
- if (index < g_strv_length (engine_names)) {
- url = ephy_search_engine_manager_build_search_address (controller->search_engine_manager,
- engine_names[index],
- content);
- ephy_link_open (EPHY_LINK (controller), url, NULL,
- ephy_link_flags_from_current_event ());
- }
-
- g_strfreev (engine_names);
- g_free (content);
- g_free (url);
-}
-
-static void
-fill_entry_completion_with_actions (GtkEntryCompletion *completion,
- EphyLocationController *controller)
-{
- char **engine_names;
-
- engine_names = ephy_search_engine_manager_get_names (controller->search_engine_manager);
-
- controller->num_search_engines_actions = 0;
-
- for (guint i = 0; engine_names[i] != NULL; i++) {
- gtk_entry_completion_insert_action_text (completion, i, engine_names[i]);
- controller->num_search_engines_actions++;
- }
-
- g_strfreev (engine_names);
-}
-
-static void
-add_completion_actions (EphyLocationController *controller,
- EphyLocationEntry *lentry)
-{
- GtkWidget *entry = ephy_location_entry_get_entry (lentry);
- GtkEntryCompletion *completion = gtk_entry_get_completion (GTK_ENTRY (entry));
-
- fill_entry_completion_with_actions (completion, controller);
- g_signal_connect (completion, "action_activated",
- G_CALLBACK (action_activated_cb), controller);
-}
-
-static void
-search_engines_changed_cb (EphySearchEngineManager *manager,
- gpointer data)
-{
- EphyLocationController *controller;
- GtkEntryCompletion *completion;
- GtkWidget *entry;
-
- controller = EPHY_LOCATION_CONTROLLER (data);
- entry = ephy_location_entry_get_entry (EPHY_LOCATION_ENTRY (controller->title_widget));
- completion = gtk_entry_get_completion (GTK_ENTRY (entry));
-
- for (guint i = 0; i < controller->num_search_engines_actions; i++)
- gtk_entry_completion_delete_action (completion, 0);
-
- fill_entry_completion_with_actions (completion, controller);
-}
-
static void
longpress_gesture_cb (GtkGestureLongPress *gesture,
gdouble x,
@@ -408,7 +307,7 @@ ephy_location_controller_constructed (GObject *object)
EphyLocationController *controller = EPHY_LOCATION_CONTROLLER (object);
EphyHistoryService *history_service;
EphyBookmarksManager *bookmarks_manager;
- EphyCompletionModel *model;
+ EphySuggestionModel *model;
GtkWidget *notebook, *widget, *reader_mode, *entry;
G_OBJECT_CLASS (ephy_location_controller_parent_class)->constructed (object);
@@ -433,28 +332,10 @@ ephy_location_controller_constructed (GObject *object)
history_service = ephy_embed_shell_get_global_history_service (ephy_embed_shell_get_default ());
bookmarks_manager = ephy_shell_get_bookmarks_manager (ephy_shell_get_default ());
- model = ephy_completion_model_new (history_service, bookmarks_manager);
- ephy_location_entry_set_completion (EPHY_LOCATION_ENTRY (controller->title_widget),
- GTK_TREE_MODEL (model),
- EPHY_COMPLETION_TEXT_COL,
- EPHY_COMPLETION_ACTION_COL,
- EPHY_COMPLETION_KEYWORDS_COL,
- EPHY_COMPLETION_RELEVANCE_COL,
- EPHY_COMPLETION_URL_COL,
- EPHY_COMPLETION_EXTRA_COL,
- EPHY_COMPLETION_FAVICON_COL);
+ model = ephy_suggestion_model_new (history_service, bookmarks_manager);
+ dzl_suggestion_entry_set_model (DZL_SUGGESTION_ENTRY (entry), G_LIST_MODEL (model));
g_object_unref (model);
- ephy_location_entry_set_match_func (EPHY_LOCATION_ENTRY (controller->title_widget),
- match_func,
- controller->title_widget,
- NULL);
-
- add_completion_actions (controller, EPHY_LOCATION_ENTRY (controller->title_widget));
-
- g_signal_connect_object (controller->search_engine_manager, "changed",
- G_CALLBACK (search_engines_changed_cb), controller, 0);
-
reader_mode = ephy_location_entry_get_reader_mode_widget (EPHY_LOCATION_ENTRY (controller->title_widget));
g_signal_connect (G_OBJECT (reader_mode), "button-press-event", G_CALLBACK
(reader_mode_button_press_event_cb), controller);
diff --git a/src/ephy-suggestion-model.c b/src/ephy-suggestion-model.c
new file mode 100644
index 000000000..f9c82a81b
--- /dev/null
+++ b/src/ephy-suggestion-model.c
@@ -0,0 +1,521 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * Copyright © 2017 Christian Hergert <chergert redhat com>
+ *
+ * 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 "config.h"
+#include "ephy-suggestion-model.h"
+
+#include "ephy-embed-shell.h"
+#include "ephy-search-engine-manager.h"
+#include "ephy-suggestion.h"
+
+#include <dazzle.h>
+#include <glib/gi18n.h>
+
+#define MAX_COMPLETION_HISTORY_URLS 8
+
+struct _EphySuggestionModel {
+ GObject parent;
+ EphyHistoryService *history_service;
+ EphyBookmarksManager *bookmarks_manager;
+ GSequence *items;
+ GSList *search_terms;
+};
+
+enum {
+ PROP_0,
+ PROP_BOOKMARKS_MANAGER,
+ PROP_HISTORY_SERVICE,
+ N_PROPS
+};
+
+static void list_model_iface_init (GListModelInterface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (EphySuggestionModel, ephy_suggestion_model, G_TYPE_OBJECT,
+ G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, list_model_iface_init))
+
+static GParamSpec *properties[N_PROPS];
+
+static void
+ephy_suggestion_model_finalize (GObject *object)
+{
+ EphySuggestionModel *self = (EphySuggestionModel *)object;
+
+ g_clear_object (&self->bookmarks_manager);
+ g_clear_object (&self->history_service);
+ g_clear_pointer (&self->items, g_sequence_free);
+
+ g_slist_free_full (self->search_terms, (GDestroyNotify)g_regex_unref);
+
+ G_OBJECT_CLASS (ephy_suggestion_model_parent_class)->finalize (object);
+}
+
+static void
+ephy_suggestion_model_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ EphySuggestionModel *self = EPHY_SUGGESTION_MODEL (object);
+
+ switch (prop_id) {
+ case PROP_HISTORY_SERVICE:
+ g_value_set_object (value, self->history_service);
+ break;
+ case PROP_BOOKMARKS_MANAGER:
+ g_value_set_object (value, self->bookmarks_manager);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+ephy_suggestion_model_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ EphySuggestionModel *self = EPHY_SUGGESTION_MODEL (object);
+
+ switch (prop_id) {
+ case PROP_HISTORY_SERVICE:
+ self->history_service = g_value_dup_object (value);
+ break;
+ case PROP_BOOKMARKS_MANAGER:
+ self->bookmarks_manager = g_value_dup_object (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+ephy_suggestion_model_class_init (EphySuggestionModelClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = ephy_suggestion_model_finalize;
+ object_class->get_property = ephy_suggestion_model_get_property;
+ object_class->set_property = ephy_suggestion_model_set_property;
+
+ properties [PROP_BOOKMARKS_MANAGER] =
+ g_param_spec_object ("bookmarks-manager",
+ "Bookmarks Manager",
+ "The bookmarks manager for suggestions",
+ EPHY_TYPE_BOOKMARKS_MANAGER,
+ (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ properties [PROP_HISTORY_SERVICE] =
+ g_param_spec_object ("history-service",
+ "History Service",
+ "The history service for suggestions",
+ EPHY_TYPE_HISTORY_SERVICE,
+ (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_properties (object_class, N_PROPS, properties);
+}
+
+static void
+ephy_suggestion_model_init (EphySuggestionModel *self)
+{
+ self->items = g_sequence_new (g_object_unref);
+}
+
+static GType
+ephy_suggestion_model_get_item_type (GListModel *model)
+{
+ return EPHY_TYPE_SUGGESTION;
+}
+
+static guint
+ephy_suggestion_model_get_n_items (GListModel *model)
+{
+ EphySuggestionModel *self = EPHY_SUGGESTION_MODEL (model);
+
+ return g_sequence_get_length (self->items);
+}
+
+static gpointer
+ephy_suggestion_model_get_item (GListModel *model,
+ guint position)
+{
+ EphySuggestionModel *self = EPHY_SUGGESTION_MODEL (model);
+ GSequenceIter *iter;
+ DzlSuggestion *suggestion;
+
+ iter = g_sequence_get_iter_at_pos (self->items, position);
+ suggestion = g_sequence_get (iter);
+
+ return g_object_ref (suggestion);
+}
+
+static void
+list_model_iface_init (GListModelInterface *iface)
+{
+ iface->get_item_type = ephy_suggestion_model_get_item_type;
+ iface->get_item = ephy_suggestion_model_get_item;
+ iface->get_n_items = ephy_suggestion_model_get_n_items;
+}
+
+EphySuggestionModel *
+ephy_suggestion_model_new (EphyHistoryService *history_service,
+ EphyBookmarksManager *bookmarks_manager)
+{
+ g_assert (EPHY_IS_HISTORY_SERVICE (history_service));
+ g_assert (EPHY_IS_BOOKMARKS_MANAGER (bookmarks_manager));
+
+ return g_object_new (EPHY_TYPE_SUGGESTION_MODEL,
+ "history-service", history_service,
+ "bookmarks-manager", bookmarks_manager,
+ NULL);
+}
+
+static void
+update_search_terms (EphySuggestionModel *self,
+ const char *text)
+{
+ const char *current;
+ const char *ptr;
+ char *tmp;
+ char *term;
+ GRegex *term_regex;
+ GRegex *quote_regex;
+ gint count;
+ gboolean inside_quotes = FALSE;
+
+ g_assert (EPHY_IS_SUGGESTION_MODEL (self));
+
+ if (self->search_terms) {
+ g_slist_free_full (self->search_terms, (GDestroyNotify)g_regex_unref);
+ self->search_terms = NULL;
+ }
+
+ quote_regex = g_regex_new ("\"", G_REGEX_OPTIMIZE,
+ G_REGEX_MATCH_NOTEMPTY, NULL);
+
+ /*
+ * This code loops through the string using pointer arythmetics.
+ * Although the string we are handling may contain UTF-8 chars
+ * this works because only ASCII chars affect what is actually
+ * copied from the string as a search term.
+ */
+ for (count = 0, current = ptr = text; ptr[0] != '\0'; ptr++, count++) {
+ /*
+ * If we found a double quote character; we will
+ * consume bytes up until the next quote, or
+ * end of line;
+ */
+ if (ptr[0] == '"')
+ inside_quotes = !inside_quotes;
+
+ /*
+ * If we found a space, and we are not looking for a
+ * closing double quote, or if the next char is the
+ * end of the string, append what we have already as
+ * a search term.
+ */
+ if (((ptr[0] == ' ') && (!inside_quotes)) || ptr[1] == '\0') {
+ /*
+ * We special-case the end of the line because
+ * we would otherwise not copy the last character
+ * of the search string, since the for loop will
+ * stop before that.
+ */
+ if (ptr[1] == '\0')
+ count++;
+
+ /*
+ * remove quotes, and quote any regex-sensitive
+ * characters
+ */
+ tmp = g_regex_escape_string (current, count);
+ term = g_regex_replace (quote_regex, tmp, -1, 0,
+ "", G_REGEX_MATCH_NOTEMPTY, NULL);
+ g_strstrip (term);
+ g_free (tmp);
+
+ /* we don't want empty search terms */
+ if (term[0] != '\0') {
+ term_regex = g_regex_new (term,
+ G_REGEX_CASELESS | G_REGEX_OPTIMIZE,
+ G_REGEX_MATCH_NOTEMPTY, NULL);
+ self->search_terms = g_slist_append (self->search_terms, term_regex);
+ }
+ g_free (term);
+
+ /* count will be incremented by the for loop */
+ count = -1;
+ current = ptr + 1;
+ }
+ }
+
+ g_regex_unref (quote_regex);
+}
+
+static gboolean
+should_add_bookmark_to_model (EphySuggestionModel *self,
+ const char *search_string,
+ const char *title,
+ const char *location)
+{
+ gboolean ret = TRUE;
+
+ if (self->search_terms) {
+ GSList *iter;
+ GRegex *current = NULL;
+
+ for (iter = self->search_terms; iter != NULL; iter = iter->next) {
+ current = (GRegex *)iter->data;
+ if ((!g_regex_match (current, title ? title : "", G_REGEX_MATCH_NOTEMPTY, NULL)) &&
+ (!g_regex_match (current, location ? location : "", G_REGEX_MATCH_NOTEMPTY, NULL))) {
+ ret = FALSE;
+ break;
+ }
+ }
+ }
+
+ return ret;
+}
+
+static void
+icon_loaded_cb (GObject *source,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ WebKitFaviconDatabase *database = WEBKIT_FAVICON_DATABASE (source);
+ EphySuggestion *suggestion = EPHY_SUGGESTION (user_data);
+ cairo_surface_t *favicon = webkit_favicon_database_get_favicon_finish (database, result, NULL);
+
+ if (favicon != NULL) {
+ gdouble x_scale, y_scale;
+ int x, y;
+
+ x = cairo_image_surface_get_width (favicon);
+ y = cairo_image_surface_get_height (favicon);
+ x_scale = (gdouble)x / 16;
+ y_scale = (gdouble)y / 16;
+
+ cairo_surface_set_device_scale (favicon, x_scale, y_scale);
+
+ ephy_suggestion_set_favicon (suggestion, favicon);
+ }
+
+ g_object_unref (suggestion);
+}
+
+static void
+load_favicon (EphySuggestion *suggestion,
+ const char *url)
+{
+ EphyEmbedShell *shell = ephy_embed_shell_get_default ();
+ WebKitWebContext *context = ephy_embed_shell_get_web_context (shell);
+ WebKitFaviconDatabase *database = webkit_web_context_get_favicon_database (context);
+
+ webkit_favicon_database_get_favicon (database, url, NULL, icon_loaded_cb, g_object_ref (suggestion));
+}
+
+static guint
+add_bookmarks (EphySuggestionModel *self,
+ const char *query)
+{
+ GSequence *bookmarks;
+ guint added = 0;
+
+ bookmarks = ephy_bookmarks_manager_get_bookmarks (self->bookmarks_manager);
+
+ for (GSequenceIter *iter = g_sequence_get_begin_iter (bookmarks);
+ !g_sequence_iter_is_end (iter);
+ iter = g_sequence_iter_next (iter)) {
+ EphyBookmark *bookmark;
+ const char *url, *title;
+
+ bookmark = g_sequence_get (iter);
+
+ url = ephy_bookmark_get_url (bookmark);
+ title = ephy_bookmark_get_title (bookmark);
+
+ if (should_add_bookmark_to_model (self, query, title, url)) {
+ EphySuggestion *suggestion;
+
+ suggestion = ephy_suggestion_new (title, url);
+ load_favicon (suggestion, url);
+
+ g_sequence_append (self->items, suggestion);
+ added++;
+ }
+ }
+
+ return added;
+}
+
+static guint
+add_history (EphySuggestionModel *self,
+ GList *urls,
+ const char *query)
+{
+ guint added = 0;
+
+ for (const GList *p = urls; p != NULL; p = p->next) {
+ EphyHistoryURL *url = (EphyHistoryURL *)p->data;
+ EphySuggestion *suggestion;
+
+ suggestion = ephy_suggestion_new (url->title, url->url);
+ load_favicon (suggestion, url->url);
+
+ g_sequence_append (self->items, suggestion);
+ added++;
+ }
+
+ return added;
+}
+
+static guint
+add_search_engines (EphySuggestionModel *self,
+ const char *query)
+{
+ EphyEmbedShell *shell;
+ EphySearchEngineManager *manager;
+ char **engines;
+ guint added = 0;
+
+ shell = ephy_embed_shell_get_default ();
+ manager = ephy_embed_shell_get_search_engine_manager (shell);
+ engines = ephy_search_engine_manager_get_names (manager);
+
+ for (guint i = 0; engines[i] != NULL; i++) {
+ EphySuggestion *suggestion;
+ char *address;
+
+ address = ephy_search_engine_manager_build_search_address (manager, engines[i], query);
+ suggestion = ephy_suggestion_new_without_subtitle (engines[i], address);
+ load_favicon (suggestion, address);
+
+ g_sequence_append (self->items, suggestion);
+ added++;
+
+ g_free (address);
+ }
+
+ g_strfreev (engines);
+
+ return added;
+}
+
+static void
+query_completed_cb (EphyHistoryService *service,
+ gboolean success,
+ gpointer result_data,
+ gpointer user_data)
+{
+ GTask *task = user_data;
+ EphySuggestionModel *self;
+ const gchar *query;
+ GList *urls;
+ guint removed;
+ guint added = 0;
+
+ self = g_task_get_source_object (task);
+ query = g_task_get_task_data (task);
+ urls = (GList *)result_data;
+
+ removed = g_sequence_get_length (self->items);
+
+ g_clear_pointer (&self->items, g_sequence_free);
+ self->items = g_sequence_new (g_object_unref);
+
+ if (strlen (query) > 0) {
+ added = add_bookmarks (self, query);
+ added += add_history (self, urls, query);
+ added += add_search_engines (self, query);
+ }
+
+ g_list_model_items_changed (G_LIST_MODEL (self), 0, removed, added);
+
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+void
+ephy_suggestion_model_query_async (EphySuggestionModel *self,
+ const gchar *query,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task = NULL;
+ char **strings;
+ GList *qlist = NULL;
+
+ g_assert (EPHY_IS_SUGGESTION_MODEL (self));
+ g_assert (query != NULL);
+ g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+ task = g_task_new (self, cancellable, callback, user_data);
+ g_task_set_source_tag (task, ephy_suggestion_model_query_async);
+ g_task_set_task_data (task, g_strdup (query), g_free);
+
+ /* Split the search string. */
+ strings = g_strsplit (query, " ", -1);
+ for (guint i = 0; strings[i]; i++)
+ qlist = g_list_append (qlist, g_strdup (strings[i]));
+
+ update_search_terms (self, query);
+
+ ephy_history_service_find_urls (self->history_service,
+ 0, 0,
+ MAX_COMPLETION_HISTORY_URLS, 0,
+ qlist,
+ EPHY_HISTORY_SORT_MOST_VISITED,
+ cancellable,
+ (EphyHistoryJobCallback)query_completed_cb,
+ task);
+
+ g_strfreev (strings);
+}
+
+gboolean
+ephy_suggestion_model_query_finish (EphySuggestionModel *self,
+ GAsyncResult *result,
+ GError **error)
+{
+ g_assert (EPHY_IS_SUGGESTION_MODEL (self));
+ g_assert (G_IS_TASK (result));
+
+ return g_task_propagate_boolean (G_TASK (result), error);
+}
+
+EphySuggestion *
+ephy_suggestion_model_get_suggestion_with_uri (EphySuggestionModel *self,
+ const char *uri)
+{
+ GSequenceIter *iter;
+
+ g_assert (EPHY_IS_SUGGESTION_MODEL (self));
+ g_assert (uri != NULL && *uri != '\0');
+
+ for (iter = g_sequence_get_begin_iter (self->items);
+ !g_sequence_iter_is_end (iter); iter = g_sequence_iter_next (iter)) {
+ EphySuggestion *suggestion;
+
+ suggestion = g_sequence_get (iter);
+ if (strcmp (ephy_suggestion_get_uri (suggestion), uri) == 0)
+ return suggestion;
+ }
+
+ return NULL;
+}
diff --git a/src/ephy-suggestion-model.h b/src/ephy-suggestion-model.h
new file mode 100644
index 000000000..ba1ff06d1
--- /dev/null
+++ b/src/ephy-suggestion-model.h
@@ -0,0 +1,46 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * Copyright © 2017 Christian Hergert <chergert redhat com>
+ *
+ * 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/>.
+ */
+
+#pragma once
+
+#include <gio/gio.h>
+
+#include "ephy-bookmarks-manager.h"
+#include "ephy-history-service.h"
+#include "ephy-suggestion.h"
+
+G_BEGIN_DECLS
+
+#define EPHY_TYPE_SUGGESTION_MODEL (ephy_suggestion_model_get_type())
+
+G_DECLARE_FINAL_TYPE (EphySuggestionModel, ephy_suggestion_model, EPHY, SUGGESTION_MODEL, GObject)
+
+EphySuggestionModel *ephy_suggestion_model_new (EphyHistoryService *history_service,
+ EphyBookmarksManager
*bookmarks_manager);
+void ephy_suggestion_model_query_async (EphySuggestionModel *self,
+ const gchar *query,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean ephy_suggestion_model_query_finish (EphySuggestionModel *self,
+ GAsyncResult *result,
+ GError **error);
+EphySuggestion *ephy_suggestion_model_get_suggestion_with_uri (EphySuggestionModel *self,
+ const char *uri);
+
+G_END_DECLS
diff --git a/src/ephy-window.c b/src/ephy-window.c
index d86c59d07..deeedaf3d 100644
--- a/src/ephy-window.c
+++ b/src/ephy-window.c
@@ -3434,7 +3434,7 @@ ephy_window_activate_location (EphyWindow *window)
title_widget = ephy_header_bar_get_title_widget (EPHY_HEADER_BAR (window->header_bar));
if (EPHY_IS_LOCATION_ENTRY (title_widget))
- ephy_location_entry_activate (EPHY_LOCATION_ENTRY (title_widget));
+ ephy_location_entry_focus (EPHY_LOCATION_ENTRY (title_widget));
}
/**
diff --git a/src/meson.build b/src/meson.build
index c45a50c24..3d4c6d137 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -25,7 +25,6 @@ libephymain_sources = [
'ephy-action-bar-end.c',
'ephy-action-bar-start.c',
'ephy-action-helper.c',
- 'ephy-completion-model.c',
'ephy-encoding-dialog.c',
'ephy-encoding-row.c',
'ephy-header-bar.c',
@@ -37,6 +36,7 @@ libephymain_sources = [
'ephy-search-engine-dialog.c',
'ephy-session.c',
'ephy-shell.c',
+ 'ephy-suggestion-model.c',
'ephy-window.c',
'passwords-dialog.c',
'popup-commands.c',
diff --git a/src/search-provider/ephy-search-provider.c b/src/search-provider/ephy-search-provider.c
index daa04f36b..97be78448 100644
--- a/src/search-provider/ephy-search-provider.c
+++ b/src/search-provider/ephy-search-provider.c
@@ -23,11 +23,12 @@
#include "ephy-search-provider.h"
#include "ephy-bookmarks-manager.h"
-#include "ephy-completion-model.h"
#include "ephy-file-helpers.h"
#include "ephy-prefs.h"
#include "ephy-profile-utils.h"
#include "ephy-shell.h"
+#include "ephy-suggestion-model.h"
+#include "ephy-uri-helpers.h"
#include <gio/gio.h>
#include <gio/gdesktopappinfo.h>
@@ -43,7 +44,7 @@ struct _EphySearchProvider {
GSettings *settings;
EphyBookmarksManager *bookmarks_manager;
- EphyCompletionModel *model;
+ EphySuggestionModel *model;
};
struct _EphySearchProviderClass {
@@ -55,29 +56,29 @@ G_DEFINE_TYPE (EphySearchProvider, ephy_search_provider, G_TYPE_APPLICATION)
#define INACTIVITY_TIMEOUT 60 * 1000 /* One minute, in milliseconds */
static void
-on_model_updated (EphyHistoryService *service,
- gboolean success,
- gpointer result_data,
- gpointer user_data)
+on_model_updated (GObject *source_object,
+ GAsyncResult *result,
+ GTask *task)
{
- GTask *task = user_data;
EphySearchProvider *self = g_task_get_source_object (task);
- GtkTreeModel *model = GTK_TREE_MODEL (self->model);
- GtkTreeIter iter;
+ EphySuggestion *suggestion;
GPtrArray *results;
const char *search_string;
- gboolean ok;
-
+ guint n_items;
+ GError *error = NULL;
results = g_ptr_array_new ();
- ok = gtk_tree_model_get_iter_first (model, &iter);
- while (ok) {
- char *result;
-
- result = gtk_tree_model_get_string_from_iter (model, &iter);
- g_ptr_array_add (results, result);
-
- ok = gtk_tree_model_iter_next (model, &iter);
+ if (ephy_suggestion_model_query_finish (self->model,
+ result,
+ &error)) {
+ n_items = g_list_model_get_n_items (G_LIST_MODEL (self->model));
+ for (guint i = 0; i < n_items; i++) {
+ suggestion = g_list_model_get_item (G_LIST_MODEL (self->model), i);
+ g_ptr_array_add (results, g_strdup (ephy_suggestion_get_uri (suggestion)));
+ }
+ } else {
+ g_warning ("Failed to query suggestion model: %s", error->message);
+ g_error_free (error);
}
search_string = g_task_get_task_data (task);
@@ -112,10 +113,11 @@ gather_results_async (EphySearchProvider *self,
search_string = g_strjoinv (" ", terms);
g_task_set_task_data (task, search_string, g_free);
- ephy_completion_model_update_for_string (self->model,
- search_string,
- on_model_updated,
- task);
+ ephy_suggestion_model_query_async (self->model,
+ search_string,
+ cancellable,
+ (GAsyncReadyCallback)on_model_updated,
+ task);
}
static void
@@ -126,7 +128,6 @@ complete_request (GObject *object,
EphySearchProvider *self = EPHY_SEARCH_PROVIDER (object);
char **results;
GError *error;
-
error = NULL;
results = gather_results_finish (self, result, &error);
@@ -177,14 +178,8 @@ handle_get_result_metas (EphyShellSearchProvider2 *skeleton,
char **results,
EphySearchProvider *self)
{
- GtkTreeModel *model = GTK_TREE_MODEL (self->model);
- GtkTreeIter iter;
int i;
GVariantBuilder builder;
- GIcon *favicon;
- char *name, *url;
- gboolean is_bookmark;
-
g_application_hold (G_APPLICATION (self));
g_cancellable_cancel (self->cancellable);
@@ -201,54 +196,28 @@ handle_get_result_metas (EphyShellSearchProvider2 *skeleton,
g_variant_builder_add (&builder, "{sv}",
"gicon", g_variant_new_string ("org.gnome.Epiphany"));
g_variant_builder_close (&builder);
- continue;
- }
-
- if (!gtk_tree_model_get_iter_from_string (model, &iter, results[i]))
- continue;
-
- gtk_tree_model_get (model, &iter,
- EPHY_COMPLETION_TEXT_COL, &name,
- EPHY_COMPLETION_URL_COL, &url,
- EPHY_COMPLETION_FAVICON_COL, &favicon,
- EPHY_COMPLETION_EXTRA_COL, &is_bookmark,
- -1);
+ } else {
+ EphySuggestion *suggestion;
+ const char *title;
+ const char *uri;
+ char *decoded_uri;
- g_variant_builder_open (&builder, G_VARIANT_TYPE ("a{sv}"));
- g_variant_builder_add (&builder, "{sv}",
- "id", g_variant_new_string (url));
- g_variant_builder_add (&builder, "{sv}",
- "name", g_variant_new_string (name));
+ suggestion = ephy_suggestion_model_get_suggestion_with_uri (self->model, results[i]);
+ title = ephy_suggestion_get_unescaped_title (suggestion);
+ uri = ephy_suggestion_get_uri (suggestion);
+ decoded_uri = ephy_uri_decode (uri);
- if (favicon == NULL) {
- char *type;
-
- type = g_content_type_from_mime_type ("text/html");
- favicon = g_content_type_get_icon (type);
-
- if (is_bookmark) {
- GEmblem *emblem;
- GIcon *emblem_icon, *emblemed;
-
- emblem_icon = g_themed_icon_new ("emblem-favorite");
- emblem = g_emblem_new (emblem_icon);
-
- emblemed = g_emblemed_icon_new (favicon, emblem);
+ g_variant_builder_open (&builder, G_VARIANT_TYPE ("a{sv}"));
+ g_variant_builder_add (&builder, "{sv}",
+ "id", g_variant_new_string (decoded_uri));
+ g_variant_builder_add (&builder, "{sv}",
+ "name", g_variant_new_string (title));
+ g_variant_builder_add (&builder, "{sv}",
+ "gicon", g_variant_new_string ("text-html"));
+ g_variant_builder_close (&builder);
- g_object_unref (emblem);
- g_object_unref (emblem_icon);
- g_object_unref (favicon);
- favicon = emblemed;
- }
+ g_free (decoded_uri);
}
-
- g_variant_builder_add (&builder, "{sv}",
- "icon", g_icon_serialize (favicon));
- g_variant_builder_close (&builder);
-
- g_object_unref (favicon);
- g_free (name);
- g_free (url);
}
ephy_shell_search_provider2_complete_get_result_metas (skeleton,
@@ -370,7 +339,7 @@ ephy_search_provider_init (EphySearchProvider *self)
filename = g_build_filename (ephy_dot_dir (), EPHY_HISTORY_FILE, NULL);
self->bookmarks_manager = ephy_bookmarks_manager_new ();
- self->model = ephy_completion_model_new (ephy_embed_shell_get_global_history_service (shell),
+ self->model = ephy_suggestion_model_new (ephy_embed_shell_get_global_history_service (shell),
self->bookmarks_manager);
g_free (filename);
diff --git a/subprojects/libdazzle.wrap b/subprojects/libdazzle.wrap
index c021f807e..15ac93ad2 100644
--- a/subprojects/libdazzle.wrap
+++ b/subprojects/libdazzle.wrap
@@ -1,4 +1,4 @@
[wrap-git]
directory=libdazzle
url=https://gitlab.gnome.org/GNOME/libdazzle.git
-revision=ace2d7a10388117b8178d253ca3a59754c5924dc
+revision=cac52ec1804c44b0c27faa8032969a60ab77cff8
diff --git a/tests/ephy-location-entry-test.c b/tests/ephy-location-entry-test.c
index c378187a5..8013e2a49 100644
--- a/tests/ephy-location-entry-test.c
+++ b/tests/ephy-location-entry-test.c
@@ -38,11 +38,13 @@ test_entry_new (void)
static void
test_entry_get_entry (void)
{
- EphyLocationEntry *entry;
+ EphyLocationEntry *lentry;
+ GtkWidget *entry;
- entry = EPHY_LOCATION_ENTRY (ephy_location_entry_new ());
+ lentry = EPHY_LOCATION_ENTRY (ephy_location_entry_new ());
+ entry = ephy_location_entry_get_entry (lentry);
- g_assert (GTK_IS_ENTRY (ephy_location_entry_get_entry (entry)));
+ g_assert (GTK_IS_ENTRY (entry));
}
static void
@@ -104,16 +106,19 @@ test_entry_can_undo (void)
{
const char *test = "test";
- EphyLocationEntry *entry;
- entry = EPHY_LOCATION_ENTRY (ephy_location_entry_new ());
+ EphyLocationEntry *lentry;
+ GtkWidget *entry;
+
+ lentry = EPHY_LOCATION_ENTRY (ephy_location_entry_new ());
- g_assert_cmpint (ephy_location_entry_get_can_undo (entry), ==, FALSE);
+ g_assert_cmpint (ephy_location_entry_get_can_undo (lentry), ==, FALSE);
/* Use gtk_* function or otherwise user_changed won't be correctly handled
* internally by the location entry (see editable_changed_cb and
* block_update) */
- gtk_entry_set_text (GTK_ENTRY (ephy_location_entry_get_entry (entry)), test);
- g_assert_cmpint (ephy_location_entry_get_can_undo (entry), ==, TRUE);
+ entry = ephy_location_entry_get_entry (lentry);
+ gtk_entry_set_text (GTK_ENTRY (entry), test);
+ g_assert_cmpint (ephy_location_entry_get_can_undo (lentry), ==, TRUE);
}
static void
diff --git a/tests/meson.build b/tests/meson.build
index 7bb567cef..6e8cc3fd8 100644
--- a/tests/meson.build
+++ b/tests/meson.build
@@ -17,15 +17,6 @@ if get_option('unit_tests')
# link_with: libephytestutils
# )
- completion_model_test = executable('test-ephy-completion-model',
- 'ephy-completion-model-test.c',
- dependencies: ephymain_dep
- )
- test('Completion model test',
- completion_model_test,
- env: envs
- )
-
# FIXME: https://bugzilla.gnome.org/show_bug.cgi?id=778153
# download_test = executable('test-ephy-download',
# 'ephy-download-test.c',
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]