[epiphany] Implement search engine dialog



commit e57ce8d3aa2a1539bb51413afa01ae31374717a4
Author: cedlemo <cedlemo gmx com>
Date:   Thu Feb 16 19:22:41 2017 +0100

    Implement search engine dialog
    
    https://bugzilla.gnome.org/show_bug.cgi?id=776738

 po/POTFILES.in                            |    2 +
 src/Makefile.am                           |    2 +
 src/ephy-search-engine-dialog.c           |  715 +++++++++++++++++++++++++++++
 src/ephy-search-engine-dialog.h           |   33 ++
 src/prefs-dialog.c                        |  135 +-----
 src/resources/epiphany.gresource.xml      |    1 +
 src/resources/gtk/prefs-dialog.ui         |   73 ++--
 src/resources/gtk/search-engine-dialog.ui |  301 ++++++++++++
 8 files changed, 1102 insertions(+), 160 deletions(-)
---
diff --git a/po/POTFILES.in b/po/POTFILES.in
index af6f0ef..e0c7154 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -36,6 +36,7 @@ src/ephy-header-bar.c
 src/ephy-history-dialog.c
 src/ephy-main.c
 src/ephy-notebook.c
+src/ephy-search-engine-dialog.c
 src/ephy-session.c
 src/ephy-shell.c
 src/ephy-window.c
@@ -55,6 +56,7 @@ src/resources/gtk/page-menu-popover.ui
 src/resources/gtk/passwords-dialog.ui
 src/resources/gtk/prefs-dialog.ui
 src/resources/gtk/prefs-lang-dialog.ui
+src/resources/gtk/search-engine-dialog.ui
 src/resources/gtk/shortcuts-dialog.ui
 src/search-provider/ephy-search-provider.c
 src/sync/ephy-sync-secret.c
diff --git a/src/Makefile.am b/src/Makefile.am
index 0c9da73..9230a3f 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -58,6 +58,8 @@ libephymain_la_SOURCES = \
        ephy-lockdown.h                         \
        ephy-notebook.c                         \
        ephy-notebook.h                         \
+       ephy-search-engine-dialog.c             \
+       ephy-search-engine-dialog.h             \
        ephy-session.c                          \
        ephy-session.h                          \
        ephy-shell.c                            \
diff --git a/src/ephy-search-engine-dialog.c b/src/ephy-search-engine-dialog.c
new file mode 100644
index 0000000..8381c48
--- /dev/null
+++ b/src/ephy-search-engine-dialog.c
@@ -0,0 +1,715 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ *  Copyright © 2017 Cedric Le Moigne <cedlemo gmx com>
+ *
+ *  This file is part of Epiphany.
+ *
+ *  Epiphany 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.
+ *
+ *  Epiphany 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 Epiphany.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include "ephy-search-engine-dialog.h"
+#include "ephy-shell.h"
+
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+#include <string.h>
+
+#define NEW_SEARCH_ENGINE_NAME     _("New search engine")
+#define NEW_SEARCH_ENGINE_ADDRESS  _("New address")
+#define NEW_SEARCH_ENGINE_BANG     _("Bang")
+
+struct _EphySearchEngineDialog {
+  GtkDialog parent_instance;
+
+  EphySearchEngineManager *search_engine_manager;
+  GtkWidget *search_engine_add_button;
+  GtkWidget *search_engine_address_entry;
+  GtkWidget *search_engine_default_switch;
+  GtkWidget *search_engine_list_box;
+  GtkWidget *search_engine_name_entry;
+  GtkWidget *search_engine_bang_entry;
+  GtkWidget *search_engine_remove_button;
+};
+
+G_DEFINE_TYPE (EphySearchEngineDialog, ephy_search_engine_dialog, GTK_TYPE_DIALOG)
+
+static GtkWidget *
+container_get_first_child (GtkContainer *container)
+{
+  GList *children;
+  GtkWidget *child;
+
+  children = gtk_container_get_children (container);
+  child = (GtkWidget *)children->data;
+  g_list_free (children);
+  return child;
+}
+
+static int
+dialog_list_box_child_n_occurence (const char             *name,
+                                   EphySearchEngineDialog *dialog)
+{
+  GList *children;
+  GList *c;
+  GtkWidget *label;
+  int count = 0;
+  const char *text;
+
+  children = gtk_container_get_children (GTK_CONTAINER (dialog->search_engine_list_box));
+
+  for (c = children; c != NULL; c = c->next) {
+    label = container_get_first_child (GTK_CONTAINER (c->data));
+    text = gtk_label_get_text (GTK_LABEL (label));
+    if (g_strcmp0 (name, text) == 0)
+      count++;
+  }
+  g_list_free (children);
+
+  return count;
+}
+
+static GtkWidget *
+add_list_box_row (EphySearchEngineDialog *dialog,
+                  const char             *name,
+                  int                     position)
+{
+  GtkListBox *listbox;
+  GtkWidget *label;
+  GtkWidget *list_box_row;
+
+  label = gtk_label_new (name);
+  gtk_widget_set_halign (label, GTK_ALIGN_START);
+
+  list_box_row = gtk_list_box_row_new ();
+  gtk_container_add (GTK_CONTAINER (list_box_row), label);
+  gtk_widget_set_size_request (list_box_row, 160, -1);
+  gtk_widget_show_all (list_box_row);
+
+  listbox = GTK_LIST_BOX (dialog->search_engine_list_box);
+  gtk_list_box_insert (listbox, list_box_row, position);
+  return list_box_row;
+}
+
+static void
+dialog_set_entry_error_state (GtkWidget *entry)
+{
+  GtkStyleContext *context;
+
+  gtk_entry_set_icon_from_icon_name (GTK_ENTRY (entry),
+                                     GTK_ENTRY_ICON_SECONDARY,
+                                     "dialog-warning-symbolic");
+  context = gtk_widget_get_style_context (entry);
+  gtk_style_context_add_class (context, "error");
+}
+
+static void
+dialog_set_entry_normal_state (GtkWidget *entry)
+{
+  GtkStyleContext *context;
+
+  gtk_entry_set_icon_from_icon_name (GTK_ENTRY (entry),
+                                     GTK_ENTRY_ICON_SECONDARY,
+                                     NULL);
+  context = gtk_widget_get_style_context (entry);
+  gtk_style_context_remove_class (context, "error");
+}
+
+static gboolean
+dialog_check_name_entry (const char             *name,
+                         EphySearchEngineDialog *dialog)
+{
+  EphySearchEngineManager *manager;
+  const char *search_engine_name;
+  GtkWidget *search_engine_label;
+  GtkListBoxRow *list_box_row;
+
+  manager = dialog->search_engine_manager;
+  list_box_row = gtk_list_box_get_selected_row (GTK_LIST_BOX (dialog->search_engine_list_box));
+  search_engine_label = container_get_first_child (GTK_CONTAINER (list_box_row));
+  search_engine_name = gtk_label_get_text (GTK_LABEL (search_engine_label));
+
+  if (name == NULL || g_strcmp0 (name, "") == 0) {
+    dialog_set_entry_error_state (dialog->search_engine_name_entry);
+    return FALSE;
+  }
+
+  /* Check if name already exist in the search_engine_manager */
+  if (ephy_search_engine_manager_get_address (manager, name) != NULL &&
+      g_strcmp0 (name, search_engine_name) != 0 ) {
+      dialog_set_entry_error_state (dialog->search_engine_name_entry);
+      return FALSE;
+  }
+
+  dialog_set_entry_normal_state (dialog->search_engine_name_entry);
+  return TRUE;
+}
+
+static gboolean
+dialog_check_address_entry (const char             *address,
+                            EphySearchEngineDialog *dialog)
+{
+  if (address == NULL || !soup_uri_new (address) || strstr (address, "%s") == NULL) {
+    dialog_set_entry_error_state (dialog->search_engine_address_entry);
+    return FALSE;
+  }
+  else
+    dialog_set_entry_normal_state (dialog->search_engine_address_entry);
+
+  return TRUE;
+}
+
+static gboolean
+dialog_check_bang_entry (const char             *bang,
+                         EphySearchEngineDialog *dialog)
+{
+  EphySearchEngineManager *manager;
+  GtkListBoxRow *list_box_row;
+  GtkWidget *search_engine_label;
+  const char *engine_from_bang;
+  const char *search_engine_name;
+
+  /* Allow empty string */
+  if (g_strcmp0 (bang, "") == 0) {
+    dialog_set_entry_normal_state (dialog->search_engine_bang_entry);
+    return TRUE;
+  }
+
+  manager = dialog->search_engine_manager;
+
+  engine_from_bang = ephy_search_engine_manager_engine_from_bang (manager, bang);
+  list_box_row = gtk_list_box_get_selected_row (GTK_LIST_BOX (dialog->search_engine_list_box));
+  search_engine_label = container_get_first_child (GTK_CONTAINER (list_box_row));
+  search_engine_name = gtk_label_get_text (GTK_LABEL (search_engine_label));
+
+  if (engine_from_bang && (g_strcmp0 (engine_from_bang, search_engine_name) != 0)) {
+    dialog_set_entry_error_state (dialog->search_engine_bang_entry);
+    return FALSE;
+  }
+  else if (g_strcmp0 (bang, NEW_SEARCH_ENGINE_BANG) == 0) {
+    dialog_set_entry_error_state (dialog->search_engine_bang_entry);
+    return FALSE;
+  }
+  else
+    dialog_set_entry_normal_state (dialog->search_engine_bang_entry);
+
+  return TRUE;
+}
+
+static void
+list_box_row_selected_cb (GtkListBox    *list_box,
+                          GtkListBoxRow *list_box_row,
+                          gpointer       data)
+{
+  EphySearchEngineDialog *dialog;
+  EphySearchEngineManager *manager;
+  GtkWidget *search_engine_label;
+  const char *search_engine_name;
+  const char *search_engine_address;
+  const char *search_engine_bang;
+  const char *search_engine_default;
+  gboolean is_default = FALSE;
+
+  if (!list_box_row)
+    return;
+
+  dialog = EPHY_SEARCH_ENGINE_DIALOG (data);
+  manager = dialog->search_engine_manager;
+
+  search_engine_label = container_get_first_child (GTK_CONTAINER (list_box_row));
+  search_engine_name = gtk_label_get_text (GTK_LABEL (search_engine_label));
+  search_engine_address = ephy_search_engine_manager_get_address (manager, search_engine_name);
+  search_engine_bang = ephy_search_engine_manager_get_bang (manager, search_engine_name);
+
+  if (!search_engine_address) {
+    gtk_entry_set_text (GTK_ENTRY (dialog->search_engine_name_entry), "");
+    gtk_entry_set_text (GTK_ENTRY (dialog->search_engine_address_entry), "");
+    gtk_entry_set_text (GTK_ENTRY (dialog->search_engine_bang_entry), "");
+    gtk_entry_set_placeholder_text (GTK_ENTRY (dialog->search_engine_address_entry),
+                                    NEW_SEARCH_ENGINE_ADDRESS);
+    gtk_entry_set_placeholder_text (GTK_ENTRY (dialog->search_engine_bang_entry),
+                                    NEW_SEARCH_ENGINE_BANG);
+    gtk_entry_set_placeholder_text (GTK_ENTRY (dialog->search_engine_name_entry),
+                                    search_engine_name);
+    search_engine_name = "";
+  }
+  else
+  {
+    gtk_entry_set_text (GTK_ENTRY (dialog->search_engine_name_entry), search_engine_name);
+    gtk_entry_set_text (GTK_ENTRY (dialog->search_engine_address_entry), search_engine_address);
+    gtk_entry_set_text (GTK_ENTRY (dialog->search_engine_bang_entry), search_engine_bang);
+
+    search_engine_default = ephy_search_engine_manager_get_default_engine (manager);
+    if (g_strcmp0 (search_engine_name, search_engine_default) == 0)
+      is_default = TRUE;
+  }
+
+  gtk_switch_set_active (GTK_SWITCH (dialog->search_engine_default_switch),
+                         is_default);
+  gtk_widget_set_sensitive (dialog->search_engine_default_switch, !is_default);
+
+  dialog_check_name_entry (search_engine_name,
+                           dialog);
+  dialog_check_address_entry (search_engine_address,
+                              dialog);
+  dialog_check_bang_entry (search_engine_bang,
+                           dialog);
+}
+
+static void
+ephy_search_engine_dialog_fill_list_box (EphySearchEngineDialog *dialog)
+{
+
+  GtkListBox *listbox;
+  GtkWidget *list_box_row;
+  EphySearchEngineManager *manager;
+  char **engines_names ;
+
+  listbox = GTK_LIST_BOX (dialog->search_engine_list_box);
+  manager = dialog->search_engine_manager;
+  engines_names = ephy_search_engine_manager_get_names (manager);
+
+  for (guint i = 0; engines_names[i] != NULL; i++) {
+    const char *name = engines_names[i];
+    list_box_row = add_list_box_row (dialog, name, i);
+    gtk_list_box_select_row (listbox, GTK_LIST_BOX_ROW (list_box_row));
+  }
+  g_strfreev (engines_names);
+
+  g_signal_connect (listbox,
+                    "row-selected",
+                    G_CALLBACK (list_box_row_selected_cb),
+                    dialog);
+  gtk_list_box_select_row (listbox,
+                           gtk_list_box_get_row_at_index (listbox, 0));
+}
+
+static char *
+generate_new_unique_default_engine_name (EphySearchEngineDialog *dialog)
+{
+  guint i = 1;
+  char *default_name = g_strdup_printf ("%s %d", NEW_SEARCH_ENGINE_NAME, i);
+
+  while (dialog_list_box_child_n_occurence (default_name, dialog) != 0)
+  {
+    if (i == UINT_MAX)
+      break;
+
+    i++;
+    g_free (default_name);
+    default_name = g_strdup_printf ("%s %d", NEW_SEARCH_ENGINE_NAME, i);
+  }
+
+  return default_name;
+}
+
+static void
+on_search_engine_add_button_clicked (GtkButton              *button,
+                                     EphySearchEngineDialog *dialog)
+{
+  GtkWidget *list_box_row;
+  char *new_engine_name;
+
+  new_engine_name = generate_new_unique_default_engine_name (dialog);
+  list_box_row = add_list_box_row (dialog, new_engine_name, -1);
+  g_free (new_engine_name);
+  gtk_list_box_select_row (GTK_LIST_BOX (dialog->search_engine_list_box),
+                           GTK_LIST_BOX_ROW (list_box_row));
+  gtk_list_box_invalidate_sort (GTK_LIST_BOX (dialog->search_engine_list_box));
+}
+
+static void
+on_search_engine_remove_button_clicked (GtkButton              *button,
+                                        EphySearchEngineDialog *dialog)
+{
+  GtkListBoxRow *list_box_row;
+  GtkListBoxRow *prev_list_box_row;
+  EphySearchEngineManager *manager;
+  GtkWidget *search_engine_label;
+  const char *default_search_engine;
+  const char *search_engine_name;
+  GList *children;
+  guint children_number;
+  guint index;
+
+  /* It should remains at least one search engine */
+  children = gtk_container_get_children (GTK_CONTAINER (dialog->search_engine_list_box));
+  children_number = g_list_length (children);
+  g_list_free (children);
+  if (children_number <= 1)
+    return;
+
+  list_box_row = gtk_list_box_get_selected_row (GTK_LIST_BOX (dialog->search_engine_list_box));
+
+  search_engine_label = container_get_first_child (GTK_CONTAINER (list_box_row));
+  search_engine_name = gtk_label_get_text (GTK_LABEL (search_engine_label));
+  manager = dialog->search_engine_manager;
+  ephy_search_engine_manager_delete_engine (manager, search_engine_name);
+
+  /* Select the previous row before removing the current one */
+  index = gtk_list_box_row_get_index (list_box_row);
+  if (index == 0)
+    index = 2; // Trick in order to select the futur first row
+  prev_list_box_row = gtk_list_box_get_row_at_index (GTK_LIST_BOX (dialog->search_engine_list_box),
+                                                     index - 1);
+  /* If the removed engine is the default, choose the newly selected as default */
+  default_search_engine = ephy_search_engine_manager_get_default_engine (dialog->search_engine_manager);
+
+  if (g_strcmp0(default_search_engine, search_engine_name) == 0) {
+    search_engine_label = container_get_first_child (GTK_CONTAINER (prev_list_box_row));
+    search_engine_name = gtk_label_get_text (GTK_LABEL (search_engine_label));
+    ephy_search_engine_manager_set_default_engine (dialog->search_engine_manager,
+                                                   search_engine_name);
+  }
+
+  gtk_list_box_select_row (GTK_LIST_BOX (dialog->search_engine_list_box),
+                           prev_list_box_row);
+
+  gtk_container_remove (GTK_CONTAINER (dialog->search_engine_list_box),
+                        GTK_WIDGET (list_box_row));
+  gtk_list_box_invalidate_sort (GTK_LIST_BOX (dialog->search_engine_list_box));
+}
+
+static gboolean
+on_default_search_engine_switch_state_modified (GtkWidget              *switch_widget,
+                                                gboolean                state,
+                                                EphySearchEngineDialog *dialog)
+{
+  GtkListBoxRow *list_box_row;
+  GtkWidget *search_engine_label;
+  const char *search_engine_name;
+  const char *default_search_engine;
+  gboolean active;
+  gboolean is_set;
+
+  active = gtk_widget_get_sensitive (switch_widget);
+  if (!active)
+    return TRUE;
+
+  list_box_row = gtk_list_box_get_selected_row (GTK_LIST_BOX (dialog->search_engine_list_box));
+  search_engine_label = container_get_first_child (GTK_CONTAINER (list_box_row));
+  search_engine_name = gtk_label_get_text (GTK_LABEL (search_engine_label));
+  default_search_engine = ephy_search_engine_manager_get_default_engine (dialog->search_engine_manager);
+
+  if ( g_strcmp0(default_search_engine, search_engine_name) != 0) {
+    is_set = ephy_search_engine_manager_set_default_engine (dialog->search_engine_manager,
+                                                            search_engine_name);
+
+    if (is_set == FALSE) {
+      gtk_switch_set_active (GTK_SWITCH (dialog->search_engine_default_switch),
+                             FALSE);
+      return FALSE;
+    }
+
+    gtk_switch_set_active (GTK_SWITCH (dialog->search_engine_default_switch),
+                           TRUE);
+    gtk_widget_set_sensitive (dialog->search_engine_default_switch, FALSE);
+    return TRUE;
+  }
+  else
+    return FALSE;
+}
+
+static void
+ephy_search_engine_dialog_class_init (EphySearchEngineDialogClass *klass)
+{
+  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+  gtk_widget_class_set_template_from_resource (widget_class,
+                                               "/org/gnome/epiphany/gtk/search-engine-dialog.ui");
+
+  gtk_widget_class_bind_template_child (widget_class,
+                                        EphySearchEngineDialog,
+                                        search_engine_add_button);
+  gtk_widget_class_bind_template_child (widget_class,
+                                        EphySearchEngineDialog,
+                                        search_engine_remove_button);
+  gtk_widget_class_bind_template_child (widget_class,
+                                        EphySearchEngineDialog,
+                                        search_engine_list_box);
+  gtk_widget_class_bind_template_child (widget_class,
+                                        EphySearchEngineDialog,
+                                        search_engine_bang_entry);
+  gtk_widget_class_bind_template_child (widget_class,
+                                        EphySearchEngineDialog,
+                                        search_engine_name_entry);
+  gtk_widget_class_bind_template_child (widget_class,
+                                        EphySearchEngineDialog,
+                                        search_engine_address_entry);
+  gtk_widget_class_bind_template_child (widget_class,
+                                        EphySearchEngineDialog,
+                                        search_engine_default_switch);
+
+  gtk_widget_class_bind_template_callback (widget_class, on_search_engine_add_button_clicked);
+  gtk_widget_class_bind_template_callback (widget_class, on_search_engine_remove_button_clicked);
+  gtk_widget_class_bind_template_callback (widget_class, on_default_search_engine_switch_state_modified);
+}
+
+static int
+sort_list_box_by_engine_name (GtkListBoxRow *row1,
+                              GtkListBoxRow *row2,
+                              gpointer       data)
+{
+  const char *row1_engine_name;
+  const char *row2_engine_name;
+  GtkWidget *label;
+
+  label = container_get_first_child (GTK_CONTAINER (row1));
+  row1_engine_name = gtk_label_get_text (GTK_LABEL (label));
+
+  label = container_get_first_child (GTK_CONTAINER (row2));
+  row2_engine_name = gtk_label_get_text (GTK_LABEL (label));
+
+  return g_strcmp0 (row1_engine_name, row2_engine_name);
+}
+
+static void
+dialog_entry_change_on_event (GtkWidget *entry,
+                              EphySearchEngineDialog *dialog)
+{
+  EphySearchEngineManager *manager;
+  GtkListBoxRow *list_box_row;
+  GtkWidget *search_engine_label;
+  const char *search_engine_name;
+  const char *search_engine_bang;
+  const char *search_engine_address;
+  const char *search_engine_selected_name;
+  gboolean valid_name = TRUE;
+  gboolean valid_address = TRUE;
+  gboolean valid_bang = TRUE;
+
+  manager = dialog->search_engine_manager;
+
+  list_box_row = gtk_list_box_get_selected_row (GTK_LIST_BOX (dialog->search_engine_list_box));
+  search_engine_label = container_get_first_child (GTK_CONTAINER (list_box_row));
+  search_engine_selected_name = gtk_label_get_text (GTK_LABEL (search_engine_label));
+  search_engine_name = gtk_entry_get_text (GTK_ENTRY (dialog->search_engine_name_entry));
+  search_engine_address = gtk_entry_get_text (GTK_ENTRY (dialog->search_engine_address_entry));
+  search_engine_bang = gtk_entry_get_text (GTK_ENTRY (dialog->search_engine_bang_entry));
+
+  /* If entry name is empty, use list box selected label */
+  if (g_strcmp0 (search_engine_name, "") == 0) {
+    search_engine_name = search_engine_selected_name;
+    gtk_entry_set_text (GTK_ENTRY (dialog->search_engine_name_entry), search_engine_name);
+  }
+  valid_name = dialog_check_name_entry (search_engine_name,
+                                        dialog);
+  valid_address = dialog_check_address_entry (search_engine_address,
+                                              dialog);
+  valid_bang = dialog_check_bang_entry (search_engine_bang,
+                                        dialog);
+  if (!valid_name || !valid_address || !valid_bang)
+    return;
+
+  if (ephy_search_engine_manager_get_address (manager, search_engine_name) == NULL) {
+    ephy_search_engine_manager_add_engine (manager,
+                                           search_engine_name,
+                                           search_engine_address,
+                                           search_engine_bang);
+  }
+  else {
+    ephy_search_engine_manager_modify_engine (manager,
+                                              search_engine_name,
+                                              search_engine_address,
+                                              search_engine_bang);
+  }
+
+  /* If the name label was not the same as the list box label, update and sort
+   * the list box */
+  search_engine_name = gtk_entry_get_text (GTK_ENTRY (dialog->search_engine_name_entry));
+  if (g_strcmp0 (search_engine_name, search_engine_selected_name) != 0) {
+    gtk_label_set_text (GTK_LABEL (search_engine_label), search_engine_name);
+    gtk_list_box_invalidate_sort (GTK_LIST_BOX (dialog->search_engine_list_box));
+  }
+  return;
+}
+
+static gboolean
+address_entry_on_focus_out_cb (GtkWidget *entry,
+                              GdkEvent  *event,
+                              gpointer   data)
+{
+  EphySearchEngineDialog *dialog;
+  dialog = EPHY_SEARCH_ENGINE_DIALOG (data);
+  dialog_entry_change_on_event (entry, dialog);
+  return FALSE;
+}
+
+static void
+address_entry_on_activate_cb (GtkWidget *entry,
+                             gpointer   data)
+{
+  EphySearchEngineDialog *dialog;
+  dialog = EPHY_SEARCH_ENGINE_DIALOG (data);
+  dialog_entry_change_on_event (entry, dialog);
+}
+
+static gboolean
+bang_entry_on_focus_out_cb (GtkWidget *entry,
+                            GdkEvent  *event,
+                            gpointer   data)
+{
+  EphySearchEngineDialog *dialog;
+  dialog = EPHY_SEARCH_ENGINE_DIALOG (data);
+  dialog_entry_change_on_event (entry, dialog);
+  return FALSE;
+}
+
+static void
+bang_entry_on_activate_cb (GtkWidget *entry,
+                           gpointer   data)
+{
+  EphySearchEngineDialog *dialog;
+  dialog = EPHY_SEARCH_ENGINE_DIALOG (data);
+  dialog_entry_change_on_event (entry, dialog);
+}
+
+static void
+name_entry_change_on_event (GtkWidget              *entry,
+                            EphySearchEngineDialog *dialog)
+{
+  EphySearchEngineManager *manager;
+  GtkListBoxRow *list_box_row;
+  GtkWidget *new_list_box_row;
+  GtkWidget *search_engine_label;
+  const char *search_engine_name;
+  const char *new_search_engine_name;
+  const char *search_engine_bang;
+  const char *search_engine_address;
+  const char *default_search_engine;
+  gboolean valid_name = TRUE;
+  gboolean valid_address = TRUE;
+  gboolean valid_bang = TRUE;
+  manager = dialog->search_engine_manager;
+
+  list_box_row = gtk_list_box_get_selected_row (GTK_LIST_BOX (dialog->search_engine_list_box));
+  search_engine_label = container_get_first_child (GTK_CONTAINER (list_box_row));
+  search_engine_name = gtk_label_get_text (GTK_LABEL (search_engine_label));
+  new_search_engine_name = gtk_entry_get_text (GTK_ENTRY (entry));
+  search_engine_address =  gtk_entry_get_text (GTK_ENTRY (dialog->search_engine_address_entry));
+  search_engine_bang = gtk_entry_get_text (GTK_ENTRY (dialog->search_engine_bang_entry));
+
+  if (g_strcmp0 (search_engine_name, new_search_engine_name) == 0) {
+    dialog_set_entry_normal_state (dialog->search_engine_name_entry);
+    return;
+  }
+
+  dialog_set_entry_normal_state (dialog->search_engine_name_entry);
+  valid_name = dialog_check_name_entry (new_search_engine_name,
+                                        dialog);
+
+  valid_address = dialog_check_address_entry (search_engine_address,
+                                              dialog);
+  valid_bang = dialog_check_bang_entry (search_engine_bang,
+                                        dialog);
+
+  if (!valid_name || !valid_address || !valid_bang)
+    return;
+
+  /* Create a new search engine */
+  ephy_search_engine_manager_add_engine (manager,
+                                         new_search_engine_name,
+                                         search_engine_address,
+                                         search_engine_bang);
+
+  /* Change the name of the default searche engine too*/
+  default_search_engine = ephy_search_engine_manager_get_default_engine (dialog->search_engine_manager);
+
+  if (g_strcmp0(default_search_engine, search_engine_name) == 0)
+    ephy_search_engine_manager_set_default_engine (dialog->search_engine_manager,
+                                                   new_search_engine_name);
+
+  ephy_search_engine_manager_delete_engine (manager, search_engine_name);
+
+  /* Add new element in the ListBox */
+  new_list_box_row = add_list_box_row (dialog,
+                                       new_search_engine_name,
+                                       -1);
+  gtk_container_remove (GTK_CONTAINER (dialog->search_engine_list_box),
+                        GTK_WIDGET (list_box_row));
+  gtk_list_box_select_row (GTK_LIST_BOX (dialog->search_engine_list_box),
+                           GTK_LIST_BOX_ROW (new_list_box_row));
+  gtk_list_box_invalidate_sort (GTK_LIST_BOX (dialog->search_engine_list_box));
+}
+
+static gboolean
+name_entry_on_focus_out_cb (GtkWidget *entry,
+                            GdkEvent  *event,
+                            gpointer   data)
+{
+  EphySearchEngineDialog *dialog;
+  dialog = EPHY_SEARCH_ENGINE_DIALOG (data);
+  name_entry_change_on_event (entry, dialog);
+  return FALSE;
+}
+
+static void
+name_entry_on_activate_cb (GtkWidget *entry,
+                           gpointer   data)
+{
+  EphySearchEngineDialog *dialog;
+  dialog = EPHY_SEARCH_ENGINE_DIALOG (data);
+  name_entry_change_on_event (entry, dialog);
+}
+
+static void
+ephy_search_engine_dialog_init (EphySearchEngineDialog *dialog)
+{
+  EphyEmbedShell *shell;
+
+  shell = ephy_embed_shell_get_default ();
+  dialog->search_engine_manager = ephy_embed_shell_get_search_engine_manager (shell);
+
+  gtk_widget_init_template (GTK_WIDGET (dialog));
+  gtk_list_box_set_sort_func (GTK_LIST_BOX (dialog->search_engine_list_box),
+                              sort_list_box_by_engine_name,
+                              NULL,
+                              NULL);
+  ephy_search_engine_dialog_fill_list_box (dialog);
+  gtk_list_box_invalidate_sort (GTK_LIST_BOX (dialog->search_engine_list_box));
+
+  g_signal_connect (dialog->search_engine_address_entry,
+                    "focus-out-event",
+                    G_CALLBACK (address_entry_on_focus_out_cb),
+                    dialog);
+  g_signal_connect (dialog->search_engine_address_entry,
+                    "activate",
+                    G_CALLBACK (address_entry_on_activate_cb),
+                    dialog);
+  g_signal_connect (dialog->search_engine_bang_entry,
+                    "focus-out-event",
+                    G_CALLBACK (bang_entry_on_focus_out_cb),
+                    dialog);
+  g_signal_connect (dialog->search_engine_bang_entry,
+                    "activate",
+                    G_CALLBACK (bang_entry_on_activate_cb),
+                    dialog);
+  g_signal_connect (dialog->search_engine_name_entry,
+                    "focus-out-event",
+                    G_CALLBACK (name_entry_on_focus_out_cb),
+                    dialog);
+  g_signal_connect (dialog->search_engine_name_entry,
+                    "activate",
+                    G_CALLBACK (name_entry_on_activate_cb),
+                    dialog);
+}
+
+EphySearchEngineDialog *
+ephy_search_engine_dialog_new (void)
+{
+  return g_object_new (EPHY_TYPE_SEARCH_ENGINE_DIALOG,
+                       "use-header-bar", TRUE,
+                       NULL);
+}
diff --git a/src/ephy-search-engine-dialog.h b/src/ephy-search-engine-dialog.h
new file mode 100644
index 0000000..55fef91
--- /dev/null
+++ b/src/ephy-search-engine-dialog.h
@@ -0,0 +1,33 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ *  Copyright © 2017 Cedric Le Moigne <cedlemo gmx com>
+ *
+ *  This file is part of Epiphany.
+ *
+ *  Epiphany 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.
+ *
+ *  Epiphany 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 Epiphany.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include "ephy-window.h"
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define EPHY_TYPE_SEARCH_ENGINE_DIALOG (ephy_search_engine_dialog_get_type ())
+G_DECLARE_FINAL_TYPE (EphySearchEngineDialog, ephy_search_engine_dialog, EPHY, SEARCH_ENGINE_DIALOG, 
GtkDialog);
+
+EphySearchEngineDialog  *ephy_search_engine_dialog_new (void);
+
+G_END_DECLS
diff --git a/src/prefs-dialog.c b/src/prefs-dialog.c
index b866e1f..8c07919 100644
--- a/src/prefs-dialog.c
+++ b/src/prefs-dialog.c
@@ -33,6 +33,7 @@
 #include "ephy-gui.h"
 #include "ephy-langs.h"
 #include "ephy-prefs.h"
+#include "ephy-search-engine-dialog.h"
 #include "ephy-session.h"
 #include "ephy-settings.h"
 #include "ephy-shell.h"
@@ -76,7 +77,6 @@ struct _PrefsDialog {
   GtkWidget *download_button_label;
   GtkWidget *automatic_downloads_checkbutton;
   GtkWidget *search_box;
-  GtkWidget *search_engine_combo;
   GtkWidget *session_box;
   GtkWidget *restore_session_checkbutton;
   GtkWidget *popups_allow_checkbutton;
@@ -148,10 +148,9 @@ typedef struct {
 #endif
 
 enum {
-  SEARCH_ENGINE_COL_NAME,
-  SEARCH_ENGINE_COL_STOCK_URL,
-  SEARCH_ENGINE_COL_URL,
-  SEARCH_ENGINE_NUM_COLS
+  COL_TITLE_ELIDED,
+  COL_ENCODING,
+  NUM_COLS
 };
 
 G_DEFINE_TYPE (PrefsDialog, prefs_dialog, GTK_TYPE_DIALOG)
@@ -576,6 +575,19 @@ on_manage_passwords_button_clicked (GtkWidget   *button,
 }
 
 static void
+on_search_engine_dialog_button_clicked (GtkWidget   *button,
+                                        PrefsDialog *dialog)
+{
+  GtkWindow *search_engine_dialog;
+
+  search_engine_dialog = GTK_WINDOW (ephy_search_engine_dialog_new ());
+
+  gtk_window_set_transient_for (search_engine_dialog, GTK_WINDOW (dialog));
+  gtk_window_set_modal (search_engine_dialog, TRUE);
+  gtk_window_present (search_engine_dialog);
+}
+
+static void
 prefs_dialog_class_init (PrefsDialogClass *klass)
 {
   GObjectClass *object_class = G_OBJECT_CLASS (klass);
@@ -593,7 +605,6 @@ prefs_dialog_class_init (PrefsDialogClass *klass)
   gtk_widget_class_bind_template_child (widget_class, PrefsDialog, custom_homepage_entry);
   gtk_widget_class_bind_template_child (widget_class, PrefsDialog, automatic_downloads_checkbutton);
   gtk_widget_class_bind_template_child (widget_class, PrefsDialog, search_box);
-  gtk_widget_class_bind_template_child (widget_class, PrefsDialog, search_engine_combo);
   gtk_widget_class_bind_template_child (widget_class, PrefsDialog, session_box);
   gtk_widget_class_bind_template_child (widget_class, PrefsDialog, restore_session_checkbutton);
   gtk_widget_class_bind_template_child (widget_class, PrefsDialog, popups_allow_checkbutton);
@@ -640,6 +651,7 @@ prefs_dialog_class_init (PrefsDialogClass *klass)
 
   gtk_widget_class_bind_template_callback (widget_class, on_manage_cookies_button_clicked);
   gtk_widget_class_bind_template_callback (widget_class, on_manage_passwords_button_clicked);
+  gtk_widget_class_bind_template_callback (widget_class, on_search_engine_dialog_button_clicked);
 }
 
 static void
@@ -1355,116 +1367,6 @@ cookies_set_mapping (const GValue       *value,
   return variant;
 }
 
-static void
-search_engine_combo_add_default_engines (GtkListStore *store)
-{
-  guint i;
-  const char *default_engines[][3] = {       /* Search engine option in the preferences dialog */
-    { N_("DuckDuckGo"),
-      "https://duckduckgo.com/?q=%s&t=epiphany";,
-      /* For the preferences dialog. Must exactly match the URL
-      * you chose in the gschema, but with & instead of &amp;
-      * If the match is not exact, there will be a spurious, ugly
-      * entry in the preferences combo, so please test this. */
-      N_("https://duckduckgo.com/?q=%s&t=epiphany";) },
-    /* Search engine option in the preferences dialog */
-    { N_("Google"),
-      "https://google.com/search?q=%s";,
-      /* For the preferences dialog. Consider a regional variant, like google.co.uk */
-      N_("https://google.com/search?q=%s";) },
-    /* Search engine option in the preferences dialog */
-    { N_("Bing"),
-      "https://www.bing.com/search?q=%s";,
-      /* For the preferences dialog. Consider a regional variant, like uk.bing.com */
-      N_("https://www.bing.com/search?q=%s";) }
-  };
-
-  for (i = 0; i < G_N_ELEMENTS (default_engines); ++i) {
-    gtk_list_store_insert_with_values (store, NULL, -1,
-                                       SEARCH_ENGINE_COL_NAME,
-                                       _(default_engines[i][0]),
-                                       SEARCH_ENGINE_COL_STOCK_URL,
-                                       default_engines[i][1],
-                                       SEARCH_ENGINE_COL_URL,
-                                       _(default_engines[i][2]),
-                                       -1);
-  }
-}
-
-/* Has the user manually set the engine to something not in the combo?
- * If so, add that URL as an extra item in the combo. */
-static void
-search_engine_combo_add_current_engine (GtkListStore *store)
-{
-  GtkTreeIter iter;
-  char *original_url;
-  gboolean in_combo = FALSE;
-  gboolean has_next = FALSE;
-
-  original_url = g_settings_get_string (EPHY_SETTINGS_MAIN,
-                                        EPHY_PREFS_KEYWORD_SEARCH_URL);
-  if (!original_url)
-    return;
-
-  has_next = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (store), &iter);
-
-  while (!in_combo && has_next) {
-    char *stock_url, *url;
-
-    gtk_tree_model_get (GTK_TREE_MODEL (store), &iter,
-                        SEARCH_ENGINE_COL_STOCK_URL, &stock_url,
-                        SEARCH_ENGINE_COL_URL, &url, -1);
-
-    if (strcmp (original_url, stock_url) == 0 ||
-        strcmp (original_url, url) == 0)
-      in_combo = TRUE;
-
-    g_free (stock_url);
-    g_free (url);
-    has_next = gtk_tree_model_iter_next (GTK_TREE_MODEL (store), &iter);
-  }
-
-  if (!in_combo)
-    gtk_list_store_insert_with_values (store, NULL, -1,
-                                       SEARCH_ENGINE_COL_NAME, original_url,
-                                       SEARCH_ENGINE_COL_STOCK_URL, original_url,
-                                       SEARCH_ENGINE_COL_URL, original_url,
-                                       -1);
-
-  g_free (original_url);
-}
-
-static void
-create_search_engine_combo (GtkComboBox *combo)
-{
-  GtkCellRenderer *renderer;
-  GtkListStore *store;
-
-  store = GTK_LIST_STORE (gtk_combo_box_get_model (combo));
-
-  search_engine_combo_add_default_engines (store);
-  search_engine_combo_add_current_engine (store);
-
-  gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (store), SEARCH_ENGINE_COL_NAME,
-                                        GTK_SORT_ASCENDING);
-  gtk_combo_box_set_model (combo, GTK_TREE_MODEL (store));
-
-  renderer = gtk_cell_renderer_text_new ();
-  gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo), renderer, TRUE);
-  gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo), renderer,
-                                  "text", SEARCH_ENGINE_COL_NAME,
-                                  NULL);
-
-  g_settings_bind_with_mapping (EPHY_SETTINGS_MAIN,
-                                EPHY_PREFS_KEYWORD_SEARCH_URL,
-                                combo, "active",
-                                G_SETTINGS_BIND_DEFAULT,
-                                combo_get_mapping,
-                                combo_set_mapping,
-                                combo,
-                                NULL);
-}
-
 static gboolean
 new_tab_homepage_get_mapping (GValue   *value,
                               GVariant *variant,
@@ -1686,7 +1588,6 @@ setup_general_page (PrefsDialog *dialog)
                     dialog);
 
   create_download_path_button (dialog);
-  create_search_engine_combo (GTK_COMBO_BOX (dialog->search_engine_combo));
 }
 
 static void
diff --git a/src/resources/epiphany.gresource.xml b/src/resources/epiphany.gresource.xml
index 0558568..8580f8f 100644
--- a/src/resources/epiphany.gresource.xml
+++ b/src/resources/epiphany.gresource.xml
@@ -26,6 +26,7 @@
     <file preprocess="xml-stripblanks" compressed="true">gtk/passwords-dialog.ui</file>
     <file preprocess="xml-stripblanks" compressed="true">gtk/prefs-dialog.ui</file>
     <file preprocess="xml-stripblanks" compressed="true">gtk/prefs-lang-dialog.ui</file>
+    <file preprocess="xml-stripblanks" compressed="true">gtk/search-engine-dialog.ui</file>
     <file preprocess="xml-stripblanks" compressed="true">gtk/shortcuts-dialog.ui</file>
   </gresource>
   <gresource prefix="/org/gnome/Epiphany/icons">
diff --git a/src/resources/gtk/prefs-dialog.ui b/src/resources/gtk/prefs-dialog.ui
index 7af529c..c22cb67 100644
--- a/src/resources/gtk/prefs-dialog.ui
+++ b/src/resources/gtk/prefs-dialog.ui
@@ -1,16 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <interface>
   <requires lib="gtk+" version="3.0"/>
-  <object class="GtkListStore" id="liststore1">
-    <columns>
-      <!-- column-name name -->
-      <column type="gchararray"/>
-      <!-- column-name stock url -->
-      <column type="gchararray"/>
-      <!-- column-name url -->
-      <column type="gchararray"/>
-    </columns>
-  </object>
   <template class="PrefsDialog" parent="GtkDialog">
     <property name="role">epiphany-preferences</property>
     <property name="modal">True</property>
@@ -169,47 +159,44 @@
                     <property name="orientation">vertical</property>
                     <property name="spacing">6</property>
                     <child>
-                      <object class="GtkLabel">
-                        <property name="visible">True</property>
-                        <property name="halign">start</property>
-                        <property name="label" translatable="yes">Search</property>
-                        <attributes>
-                          <attribute name="weight" value="bold"/>
-                        </attributes>
-                      </object>
-                    </child>
-                    <child>
                       <object class="GtkBox">
                         <property name="visible">True</property>
-                        <property name="orientation">vertical</property>
-                        <property name="spacing">6</property>
-                        <property name="margin-start">12</property>
                         <child>
-                          <object class="GtkBox">
+                          <object class="GtkLabel">
                             <property name="visible">True</property>
-                            <property name="spacing">12</property>
-                            <child>
-                              <object class="GtkLabel">
-                                <property name="visible">True</property>
-                                <property name="label" translatable="yes">_Engine:</property>
-                                <property name="use-underline">True</property>
-                                <property name="mnemonic-widget">search_engine_combo</property>
-                              </object>
-                            </child>
-                            <child>
-                              <object class="GtkComboBox" id="search_engine_combo">
-                                <property name="visible">True</property>
-                                <property name="model">liststore1</property>
-                              </object>
-                              <packing>
-                                <property name="expand">True</property>
-                                <property name="position">1</property>
-                              </packing>
-                            </child>
+                            <property name="halign">start</property>
+                            <property name="hexpand">True</property>
+                            <property name="label" translatable="yes">Search Engines</property>
+                            <attributes>
+                              <attribute name="weight" value="bold"/>
+                            </attributes>
+                          </object>
+                        </child>
+                       <child>
+                          <object class="GtkButton" id="search_engine_dialog_button">
+                            <property name="label" translatable="yes">_Manage Search Engines…</property>
+                            <property name="visible">True</property>
+                            <property name="use-underline">True</property>
+                            <signal name="clicked" handler="on_search_engine_dialog_button_clicked"/>
                           </object>
                         </child>
                       </object>
                     </child>
+                   <child>
+                     <object class="GtkLabel">
+                        <property name="visible">True</property>
+                       <property name="margin_left">12</property>
+                        <property name="halign">start</property>
+                        <property name="label" translatable="yes">You can select different search engines to 
use.</property>
+                        <attributes>
+                        </attributes>
+                      </object>
+                     <packing>
+                       <property name="expand">False</property>
+                       <property name="fill">False</property>
+                       <property name="position">1</property>
+                     </packing>
+                   </child>
                   </object>
                 </child>
                 <child>
diff --git a/src/resources/gtk/search-engine-dialog.ui b/src/resources/gtk/search-engine-dialog.ui
new file mode 100644
index 0000000..2e7d630
--- /dev/null
+++ b/src/resources/gtk/search-engine-dialog.ui
@@ -0,0 +1,301 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <requires lib="gtk+" version="3.10"/>
+    <template class="EphySearchEngineDialog" parent="GtkDialog">
+      <property name="modal">True</property>
+      <property name="window_position">center</property>
+      <property name="destroy_with_parent">True</property>
+      <property name="type_hint">dialog</property>
+      <child internal-child="headerbar">
+        <object class="GtkHeaderBar">
+          <property name="title" translatable="yes">Manage Search Engines</property>
+          <property name="show-close-button">True</property>
+        </object>
+      </child>
+    <child internal-child="vbox">
+      <object class="GtkBox">
+        <property name="can_focus">False</property>
+        <child internal-child="action_area">
+          <object class="GtkButtonBox">
+            <property name="can_focus">False</property>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">False</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkBox">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="margin">12</property>
+            <child>
+              <object class="GtkBox">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="orientation">vertical</property>
+                <child>
+                  <object class="GtkScrolledWindow">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="hscrollbar_policy">never</property>
+                    <property name="shadow_type">in</property>
+                    <child>
+                      <object class="GtkViewport">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <child>
+                          <object class="GtkListBox" id="search_engine_list_box">
+                            <property name="visible">True</property>
+                            <property name="can_focus">False</property>
+                           <property name="selection-mode">single</property>
+                          </object>
+                        </child>
+                      </object>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="expand">True</property>
+                    <property name="fill">True</property>
+                    <property name="position">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkBox">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <child>
+                      <object class="GtkButton" id="search_engine_add_button">
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                        <property name="receives_default">True</property>
+                       <signal name="clicked" handler="on_search_engine_add_button_clicked"/>
+                        <child>
+                          <object class="GtkImage">
+                            <property name="visible">True</property>
+                            <property name="can_focus">False</property>
+                            <property name="icon_name">list-add-symbolic</property>
+                          </object>
+                        </child>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="fill">True</property>
+                        <property name="position">0</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkButton" id="search_engine_remove_button">
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                        <property name="receives_default">True</property>
+                       <signal name="clicked" handler="on_search_engine_remove_button_clicked"/>
+                        <child>
+                          <object class="GtkImage">
+                            <property name="visible">True</property>
+                            <property name="can_focus">False</property>
+                            <property name="icon_name">list-remove-symbolic</property>
+                          </object>
+                        </child>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="fill">True</property>
+                        <property name="position">1</property>
+                      </packing>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">True</property>
+                    <property name="position">1</property>
+                  </packing>
+                </child>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkBox">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="orientation">vertical</property>
+                <property name="spacing">12</property>
+                <child>
+                  <object class="GtkGrid">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="halign">center</property>
+                    <property name="row_spacing">6</property>
+                    <property name="column_spacing">12</property>
+                    <child>
+                      <object class="GtkLabel">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="label" translatable="yes">Name</property>
+                        <property name="justify">right</property>
+                        <property name="margin">6</property>
+                      </object>
+                      <packing>
+                        <property name="left_attach">0</property>
+                        <property name="top_attach">0</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkLabel">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="label" translatable="yes">Address</property>
+                        <property name="justify">right</property>
+                        <property name="margin">6</property>
+                      </object>
+                      <packing>
+                        <property name="left_attach">0</property>
+                        <property name="top_attach">1</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkEntry" id="search_engine_name_entry">
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                        <property name="width_chars">40</property>
+                      </object>
+                      <packing>
+                        <property name="left_attach">1</property>
+                        <property name="top_attach">0</property>
+                       <property name="width">3</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkLabel">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="label" translatable="yes">Bang</property>
+                        <property name="justify">right</property>
+                        <property name="margin">6</property>
+                      </object>
+                      <packing>
+                        <property name="left_attach">2</property>
+                        <property name="top_attach">2</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkEntry" id="search_engine_bang_entry">
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                      </object>
+                      <packing>
+                        <property name="left_attach">3</property>
+                        <property name="top_attach">2</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkEntry" id="search_engine_address_entry">
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                        <property name="width_chars">40</property>
+                      </object>
+                      <packing>
+                        <property name="left_attach">1</property>
+                        <property name="top_attach">1</property>
+                       <property name="width">3</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkLabel">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="label" translatable="yes">Default</property>
+                        <property name="justify">right</property>
+                        <property name="margin">6</property>
+                      </object>
+                      <packing>
+                        <property name="left_attach">0</property>
+                        <property name="top_attach">2</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkSwitch" id="search_engine_default_switch">
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                       <property name="halign">start</property>
+                        <signal name="notify::active" 
handler="on_default_search_engine_switch_state_modified"/>
+                      </object>
+                      <packing>
+                        <property name="left_attach">1</property>
+                        <property name="top_attach">2</property>
+                      </packing>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">True</property>
+                    <property name="position">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkBox">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <child>
+                      <object class="GtkImage">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="icon_name">dialog-information-symbolic</property>
+                        <property name="icon_size">6</property>
+                       <property name="margin_left">6</property>
+                       <property name="margin_right">6</property>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="fill">True</property>
+                        <property name="position">0</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkLabel">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="valign">end</property>
+                        <property name="label" translatable="yes">To determine the search address, perform a 
search using the search engine that you want to add and check the resulting address. Remove the search term 
from the resulting address and replace it with %s.</property>
+                        <property name="wrap">True</property>
+                        <property name="max_width_chars">40</property>
+                      </object>
+                      <packing>
+                        <property name="expand">True</property>
+                        <property name="fill">True</property>
+                        <property name="position">1</property>
+                      </packing>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">True</property>
+                    <property name="position">2</property>
+                  </packing>
+                </child>
+              </object>
+              <packing>
+                <property name="expand">True</property>
+                <property name="fill">True</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">True</property>
+            <property name="position">1</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+    <child>
+      <placeholder/>
+    </child>
+  </template>
+</interface>


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