[epiphany] history-dialog: Add selection mode



commit c4aa0fb63aafb8ec93921cee84dae64ef6f5bfde
Author: Yetizone <andreii lisita gmail com>
Date:   Sat Jul 4 18:48:24 2020 +0300

    history-dialog: Add selection mode
    
    - Add copy url buttons to History rows
    - Remove the right click popup menu
    - Move Clear All button to an action bar at the bottom
    
    Closes https://gitlab.gnome.org/GNOME/epiphany/-/issues/903

 src/ephy-history-dialog.c           | 414 +++++++++++++++++++++---------------
 src/preferences/ephy-data-dialog.c  | 160 +++++++++++++-
 src/preferences/ephy-data-dialog.h  |  10 +
 src/resources/gtk/data-dialog.ui    | 182 +++++++++++++---
 src/resources/gtk/history-dialog.ui |  44 +---
 5 files changed, 559 insertions(+), 251 deletions(-)
---
diff --git a/src/ephy-history-dialog.c b/src/ephy-history-dialog.c
index e1356348a..2065ce4eb 100644
--- a/src/ephy-history-dialog.c
+++ b/src/ephy-history-dialog.c
@@ -54,7 +54,6 @@ struct _EphyHistoryDialog {
 
   GtkWidget *listbox;
   GtkWidget *forget_all_button;
-  GtkWidget *popup_menu;
 
   GActionGroup *action_group;
 
@@ -62,6 +61,7 @@ struct _EphyHistoryDialog {
   guint sorter_source;
 
   gint num_fetch;
+  gboolean shift_modifier_active;
 
   GtkWidget *confirmation_dialog;
 };
@@ -178,23 +178,21 @@ filter_now (EphyHistoryDialog *self)
 }
 
 static GList *
-get_selection (EphyHistoryDialog *self)
+get_checked_rows (EphyHistoryDialog *self)
 {
-  GList *selected_rows = gtk_list_box_get_selected_rows (GTK_LIST_BOX (self->listbox));
-  GList *list = NULL;
-  GList *tmp;
+  g_autoptr (GList) rows_list = gtk_container_get_children (GTK_CONTAINER (self->listbox));
+  GList *checked_rows = NULL;
+  GList *iter = NULL;
 
-  for (tmp = selected_rows; tmp; tmp = tmp->next) {
-    EphyHistoryURL *url = get_url_from_row (tmp->data);
+  for (iter = rows_list; iter != NULL; iter = g_list_next (iter)) {
+    GObject *row = iter->data;
+    GtkCheckButton *check_button = GTK_CHECK_BUTTON (g_object_get_data (row, "check-button"));
 
-    if (!url) {
-      continue;
-    }
-
-    list = g_list_append (list, url);
+    if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (check_button)))
+      checked_rows = g_list_prepend (checked_rows, row);
   }
 
-  return g_list_reverse (list);
+  return checked_rows;
 }
 
 static void
@@ -212,28 +210,71 @@ on_browse_history_deleted_cb (gpointer service,
 }
 
 static void
-delete_selected (EphyHistoryDialog *self)
+delete_checked_rows (EphyHistoryDialog *self)
 {
-  GList *selected;
+  g_autoptr (GList) checked_rows = get_checked_rows (self);
+  GList *deleted_urls = NULL;
+  GList *iter = NULL;
+
+  for (iter = checked_rows; iter != NULL; iter = g_list_next (iter)) {
+    EphyHistoryURL *url = get_url_from_row (iter->data);
+
+    deleted_urls = g_list_prepend (deleted_urls, url);
+  }
 
-  selected = get_selection (self);
-  ephy_history_service_delete_urls (self->history_service, selected, self->cancellable,
+  ephy_history_service_delete_urls (self->history_service, deleted_urls, self->cancellable,
                                     (EphyHistoryJobCallback)on_browse_history_deleted_cb, self);
 
-  for (GList *l = selected; l; l = l->next)
-    ephy_snapshot_service_delete_snapshot_for_url (self->snapshot_service, ((EphyHistoryURL *)l->data)->url);
+  for (iter = deleted_urls; iter != NULL; iter = g_list_next (iter))
+    ephy_snapshot_service_delete_snapshot_for_url (self->snapshot_service, ((EphyHistoryURL 
*)iter->data)->url);
+
+  ephy_data_dialog_set_selection_actions_sensitive (EPHY_DATA_DIALOG (self), FALSE);
+  g_list_free_full (deleted_urls, (GDestroyNotify)ephy_history_url_free);
+}
+
+static GtkWidget *
+get_target_window (EphyHistoryDialog *self)
+{
+  return GTK_WIDGET (gtk_application_get_active_window (GTK_APPLICATION (ephy_shell_get_default ())));
 }
 
 static void
-forget_clicked (GtkButton *button,
-                gpointer   user_data)
+open_checked_rows (GtkWidget         *open_button,
+                   EphyHistoryDialog *self)
 {
-  EphyHistoryDialog *self = EPHY_HISTORY_DIALOG (user_data);
-  GtkListBoxRow *row = g_object_get_data (G_OBJECT (button), "row");
+  EphyWindow *window = EPHY_WINDOW (get_target_window (self));
+  g_autoptr (GList) checked_rows = get_checked_rows (self);
+  GList *iter = NULL;
+
+  for (iter = checked_rows; iter != NULL; iter = g_list_next (iter)) {
+    g_autoptr (EphyHistoryURL) url = get_url_from_row (iter->data);
+    EphyEmbed *embed;
+
+    embed = ephy_shell_new_tab (ephy_shell_get_default (),
+                                window, NULL, EPHY_NEW_TAB_JUMP);
+    ephy_web_view_load_url (ephy_embed_get_web_view (embed), url->url);
+  }
+}
 
-  gtk_list_box_select_row (GTK_LIST_BOX (self->listbox), row);
+static void
+row_copy_url_button_clicked (GtkWidget *button,
+                             gpointer   user_data)
+{
+  GtkListBoxRow *row = user_data;
+  g_autoptr (EphyHistoryURL) url = get_url_from_row (row);
 
-  delete_selected (self);
+  if (url)
+    gtk_clipboard_set_text (gtk_widget_get_clipboard (GTK_WIDGET (button), GDK_SELECTION_CLIPBOARD), 
url->url, -1);
+}
+
+static void
+row_check_button_toggled (GtkCheckButton    *check_button,
+                          EphyHistoryDialog *self)
+{
+  g_autoptr (GList) checked_rows = get_checked_rows (self);
+  guint n_rows = g_list_length (checked_rows);
+
+  ephy_data_dialog_set_selection_actions_sensitive (EPHY_DATA_DIALOG (self), n_rows > 0);
 }
 
 static GtkWidget *
@@ -244,7 +285,8 @@ create_row (EphyHistoryDialog *self,
   GtkWidget *date;
   GtkWidget *row;
   GtkWidget *separator;
-  GtkWidget *button;
+  GtkWidget *check_button;
+  GtkWidget *copy_url_button;
 
   /* Row */
   row = hdy_action_row_new ();
@@ -263,23 +305,44 @@ create_row (EphyHistoryDialog *self,
   gtk_widget_set_margin_top (separator, 8);
   gtk_widget_set_margin_bottom (separator, 8);
 
-  /* Button */
-  button = gtk_button_new_from_icon_name ("user-trash-symbolic", GTK_ICON_SIZE_BUTTON);
-  gtk_widget_set_valign (button, GTK_ALIGN_CENTER);
-  g_object_set_data (G_OBJECT (button), "row", row);
-  gtk_widget_set_tooltip_text (button, _("Remove the selected pages from history"));
-  g_signal_connect (button, "clicked", G_CALLBACK (forget_clicked), self);
-  gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE);
-
-  /* Added in reverse order because actions are packed from the end. */
+  /* CheckButton */
+  check_button = gtk_check_button_new ();
+  g_object_set_data (G_OBJECT (row), "check-button", check_button);
+  gtk_widget_set_valign (check_button, GTK_ALIGN_CENTER);
+  gtk_widget_set_tooltip_text (check_button, _("Remove the selected pages from history"));
+  gtk_button_set_relief (GTK_BUTTON (check_button), GTK_RELIEF_NONE);
+  g_signal_connect (check_button, "toggled", G_CALLBACK (row_check_button_toggled), self);
+
+  /* Copy URL button */
+  copy_url_button = gtk_button_new_from_icon_name ("edit-copy-symbolic", GTK_ICON_SIZE_BUTTON);
+  gtk_widget_set_valign (copy_url_button, GTK_ALIGN_CENTER);
+  gtk_widget_set_tooltip_text (copy_url_button, _("Copy URL"));
+  g_signal_connect (copy_url_button, "clicked", G_CALLBACK (row_copy_url_button_clicked), row);
+
+  /* Separator and CheckButton should be visible only during selection mode */
+  g_object_bind_property (self, "selection-active",
+                          separator, "visible",
+                          G_BINDING_DEFAULT);
+
+  g_object_bind_property (self, "selection-active",
+                          check_button, "visible",
+                          G_BINDING_DEFAULT);
+
+  hdy_action_row_add_prefix (HDY_ACTION_ROW (row), check_button);
+  hdy_action_row_add_prefix (HDY_ACTION_ROW (row), separator);
   gtk_container_add (GTK_CONTAINER (row), date);
-  gtk_container_add (GTK_CONTAINER (row), separator);
-  gtk_container_add (GTK_CONTAINER (row), button);
+  gtk_container_add (GTK_CONTAINER (row), copy_url_button);
 
-  gtk_widget_set_sensitive (button, ephy_embed_shell_get_mode (shell) != EPHY_EMBED_SHELL_MODE_INCOGNITO);
+  gtk_widget_set_sensitive (check_button, ephy_embed_shell_get_mode (shell) != 
EPHY_EMBED_SHELL_MODE_INCOGNITO);
 
   gtk_widget_show_all (row);
 
+  /* Hide the Separator and CheckButton if selection isn't active */
+  if (!ephy_data_dialog_get_selection_active (EPHY_DATA_DIALOG (self))) {
+    gtk_widget_set_visible (separator, FALSE);
+    gtk_widget_set_visible (check_button, FALSE);
+  }
+
   return row;
 }
 
@@ -394,73 +457,13 @@ forget_all (GSimpleAction *action,
   gtk_widget_show (self->confirmation_dialog);
 }
 
-static GtkWidget *
-get_target_window (EphyHistoryDialog *self)
-{
-  return GTK_WIDGET (gtk_application_get_active_window (GTK_APPLICATION (ephy_shell_get_default ())));
-}
-
-static void
-open_selection (GSimpleAction *action,
-                GVariant      *parameter,
-                gpointer       user_data)
-{
-  EphyHistoryDialog *self = EPHY_HISTORY_DIALOG (user_data);
-  EphyWindow *window;
-  GList *selection;
-  GList *l;
-
-  selection = get_selection (self);
-
-  window = EPHY_WINDOW (get_target_window (self));
-  for (l = selection; l; l = l->next) {
-    EphyHistoryURL *url = l->data;
-    EphyEmbed *embed;
-
-    embed = ephy_shell_new_tab (ephy_shell_get_default (),
-                                window, NULL, EPHY_NEW_TAB_JUMP);
-    ephy_web_view_load_url (ephy_embed_get_web_view (embed), url->url);
-  }
-
-  g_list_free_full (selection, (GDestroyNotify)ephy_history_url_free);
-}
-
-static void
-copy_url (GSimpleAction *action,
-          GVariant      *parameter,
-          gpointer       user_data)
-{
-  EphyHistoryDialog *self = EPHY_HISTORY_DIALOG (user_data);
-  GList *selection;
-
-  selection = get_selection (self);
-
-  if (g_list_length (selection) == 1) {
-    EphyHistoryURL *url = selection->data;
-    gtk_clipboard_set_text (gtk_clipboard_get_default (gdk_display_get_default ()),
-                            url->url, -1);
-  }
-
-  g_list_free_full (selection, (GDestroyNotify)ephy_history_url_free);
-}
-
-static void
-forget (GSimpleAction *action,
-        GVariant      *parameter,
-        gpointer       user_data)
-{
-  EphyHistoryDialog *self = EPHY_HISTORY_DIALOG (user_data);
-
-  delete_selected (self);
-}
-
 static gboolean
 on_listbox_key_press_event (GtkWidget         *widget,
                             GdkEventKey       *event,
                             EphyHistoryDialog *self)
 {
   if (event->keyval == GDK_KEY_Delete || event->keyval == GDK_KEY_KP_Delete) {
-    delete_selected (self);
+    delete_checked_rows (self);
 
     return TRUE;
   }
@@ -468,17 +471,6 @@ on_listbox_key_press_event (GtkWidget         *widget,
   return FALSE;
 }
 
-static void
-update_popup_menu_actions (GActionGroup *action_group,
-                           gboolean      only_one_selected_item)
-{
-  GAction *copy_url_action;
-
-  copy_url_action = g_action_map_lookup_action (G_ACTION_MAP (action_group), "copy-url");
-
-  g_simple_action_set_enabled (G_SIMPLE_ACTION (copy_url_action), only_one_selected_item);
-}
-
 static void
 on_search_text_changed (EphyHistoryDialog *self)
 {
@@ -501,6 +493,11 @@ on_key_press_event (EphyHistoryDialog *self,
 {
   GdkEventKey *key = (GdkEventKey *)event;
 
+  /* Keep track internally of the Shift modifier needed for the
+   * interval selection logic */
+  if (key->keyval == GDK_KEY_Shift_L || key->keyval == GDK_KEY_Shift_R)
+    self->shift_modifier_active = TRUE;
+
   if (key->keyval == GDK_KEY_Down || key->keyval == GDK_KEY_Page_Down) {
     GList *childrens = gtk_container_get_children (GTK_CONTAINER (self->listbox));
     GtkWidget *last = g_list_last (childrens)->data;
@@ -513,82 +510,126 @@ on_key_press_event (EphyHistoryDialog *self,
     }
   }
 
-  return GDK_EVENT_PROPAGATE;
-}
+  /* Edge case: Shift + Enter in selection mode
+   * Pressing simply Enter without any modifiers activates the focused row,
+   * but pressing Enter with modifiers doesn't do anything.
+   * We want Shift + Enter to activate the row and trigger the
+   * row interval selecton logic */
+  if (key->keyval == GDK_KEY_Return && self->shift_modifier_active
+      && ephy_data_dialog_get_selection_active (EPHY_DATA_DIALOG (self))) {
+    GtkWindow *dialog_window = GTK_WINDOW (self);
+    GtkWidget *focused_widget = gtk_window_get_focus (dialog_window);
 
-static void
-update_selection_actions (GActionGroup *action_group,
-                          gboolean      has_selection)
-{
-  EphyEmbedShell *shell = ephy_embed_shell_get_default ();
-  GAction *forget_action;
-  GAction *open_selection_action;
+    if (GTK_IS_LIST_BOX_ROW (focused_widget)) {
+      g_signal_emit_by_name (self->listbox, "row-activated", focused_widget, self);
 
-  if (ephy_embed_shell_get_mode (shell) != EPHY_EMBED_SHELL_MODE_INCOGNITO) {
-    forget_action = g_action_map_lookup_action (G_ACTION_MAP (action_group), "forget");
-    g_simple_action_set_enabled (G_SIMPLE_ACTION (forget_action), has_selection);
+      return GDK_EVENT_STOP;
+    }
   }
 
-  open_selection_action = g_action_map_lookup_action (G_ACTION_MAP (action_group), "open-selection");
-  g_simple_action_set_enabled (G_SIMPLE_ACTION (open_selection_action), has_selection);
+  return GDK_EVENT_PROPAGATE;
 }
 
-static void
-on_listbox_row_selected (GtkListBox        *box,
-                         GtkListBoxRow     *row,
-                         EphyHistoryDialog *self)
+static gboolean
+on_key_release_event (EphyHistoryDialog *self,
+                      GdkEvent          *event,
+                      gpointer           user_data)
 {
-  update_selection_actions (self->action_group, !!row);
+  GdkEventKey *key = (GdkEventKey *)event;
+
+  /* Keep track internally of the Shift modifier needed for the
+   * interval selection logic */
+  if (key->keyval == GDK_KEY_Shift_L || key->keyval == GDK_KEY_Shift_R)
+    self->shift_modifier_active = FALSE;
+
+  /* Don't handle the event */
+  return GDK_EVENT_PROPAGATE;
 }
 
 static void
-on_listbox_row_activated (GtkListBox        *box,
-                          GtkListBoxRow     *row,
-                          EphyHistoryDialog *self)
+check_rows_interval (GtkListBox *listbox,
+                     gint        index_a,
+                     gint        index_b)
 {
-  EphyWindow *window;
-  EphyHistoryURL *url;
-  EphyEmbed *embed;
+  gint start = 0;
+  gint end = 0;
+  gint index = 0;
 
-  window = EPHY_WINDOW (get_target_window (self));
-  url = get_url_from_row (row);
-  g_assert (url);
+  if (index_a < index_b) {
+    start = index_a;
+    end = index_b;
+  } else {
+    start = index_b;
+    end = index_a;
+  }
 
-  embed = ephy_shell_new_tab (ephy_shell_get_default (),
-                              window, NULL, EPHY_NEW_TAB_JUMP);
-  ephy_web_view_load_url (ephy_embed_get_web_view (embed), url->url);
-  ephy_history_url_free (url);
+  for (index = start; index <= end; index++) {
+    GtkListBoxRow *row = gtk_list_box_get_row_at_index (listbox, index);
+    GtkCheckButton *check_button = GTK_CHECK_BUTTON (g_object_get_data (G_OBJECT (row), "check-button"));
+
+    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check_button), TRUE);
+  }
 }
 
-static gboolean
-on_listbox_button_press_event (GtkWidget         *widget,
-                               GdkEventButton    *event,
-                               EphyHistoryDialog *self)
+static void
+handle_selection_row_activated_event (EphyHistoryDialog *self,
+                                      GtkListBoxRow     *activated_row)
 {
-  if (event->button == GDK_BUTTON_SECONDARY) {
-    GtkListBoxRow *row = gtk_list_box_get_row_at_y (GTK_LIST_BOX (self->listbox), event->y);
-    GList *rows = NULL;
-    int n;
+  g_autoptr (GList) checked_rows = get_checked_rows (self);
+  GtkCheckButton *check_button = GTK_CHECK_BUTTON (g_object_get_data (G_OBJECT (activated_row), 
"check-button"));
+  gboolean button_checked = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (check_button));
 
-    if (!row)
-      return GDK_EVENT_PROPAGATE;
+  /* If Shift modifier isn't active, event simply toggles the row's checkbox button */
+  if (!self->shift_modifier_active) {
+    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check_button), !button_checked);
+    return;
+  }
 
-    if (!gtk_list_box_row_is_selected (row))
-      gtk_list_box_unselect_all (GTK_LIST_BOX (self->listbox));
+  /* If Shift modifier is active, do the row interval logic */
+  if (g_list_length (checked_rows) == 1) {
+    /* If there's exactly one other row checked we check the interval between
+     * that one and the currently clicked row */
+    gint index_a = gtk_list_box_row_get_index (activated_row);
+    gint index_b = gtk_list_box_row_get_index (checked_rows->data);
 
-    gtk_list_box_select_row (GTK_LIST_BOX (self->listbox), row);
-    rows = gtk_list_box_get_selected_rows (GTK_LIST_BOX (self->listbox));
-    n = g_list_length (rows);
-    g_list_free (rows);
+    check_rows_interval (GTK_LIST_BOX (self->listbox), index_a, index_b);
+  } else {
+    /* If there are zero or more than one other rows checked,
+     * then we check the clicked row and uncheck all the others */
+    g_autoptr (GList) rows = gtk_container_get_children (GTK_CONTAINER (self->listbox));
+    GList *iter = NULL;
 
-    update_popup_menu_actions (self->action_group, n == 1);
+    for (iter = rows; iter != NULL; iter = g_list_next (iter)) {
+      GObject *row = iter->data;
+      GtkCheckButton *row_check_btn = GTK_CHECK_BUTTON (g_object_get_data (row, "check-button"));
 
-    gtk_menu_popup_at_pointer (GTK_MENU (self->popup_menu), (GdkEvent *)event);
+      gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (row_check_btn), FALSE);
+    }
 
-    return GDK_EVENT_STOP;
+    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check_button), TRUE);
   }
+}
 
-  return GDK_EVENT_PROPAGATE;
+static void
+on_listbox_row_activated (GtkListBox        *box,
+                          GtkListBoxRow     *row,
+                          EphyHistoryDialog *self)
+{
+  gboolean selection_active = ephy_data_dialog_get_selection_active (EPHY_DATA_DIALOG (self));
+
+  /* If a History row is activated outside of selection mode, we open the
+   * row's web page in a new tab*/
+  if (!selection_active) {
+    EphyWindow *window = EPHY_WINDOW (get_target_window (self));
+    g_autoptr (EphyHistoryURL) url = get_url_from_row (row);
+    EphyEmbed *embed = ephy_shell_new_tab (ephy_shell_get_default (),
+                                           window, NULL, EPHY_NEW_TAB_JUMP);
+
+    ephy_web_view_load_url (ephy_embed_get_web_view (embed), url->url);
+  } else {
+    /* Selection mode is active, run selection logic */
+    handle_selection_row_activated_event (self, row);
+  }
 }
 
 static void
@@ -671,6 +712,27 @@ on_edge_reached (GtkScrolledWindow *scrolled,
   }
 }
 
+static void
+on_dialog_selection_active_toggled (EphyHistoryDialog *self)
+{
+  /* Uncheck all rows */
+  g_autoptr (GList) rows = gtk_container_get_children (GTK_CONTAINER (self->listbox));
+  GList *iter = NULL;
+
+  for (iter = rows; iter != NULL; iter = g_list_next (iter)) {
+    GObject *row = iter->data;
+    GtkCheckButton *check_button = GTK_CHECK_BUTTON (g_object_get_data (row, "check-button"));
+
+    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check_button), FALSE);
+  }
+}
+
+static void
+on_dialog_selection_delete_clicked (EphyHistoryDialog *self)
+{
+  delete_checked_rows (self);
+}
+
 static void
 ephy_history_dialog_class_init (EphyHistoryDialogClass *klass)
 {
@@ -693,14 +755,14 @@ ephy_history_dialog_class_init (EphyHistoryDialogClass *klass)
   gtk_widget_class_set_template_from_resource (widget_class,
                                                "/org/gnome/epiphany/gtk/history-dialog.ui");
   gtk_widget_class_bind_template_child (widget_class, EphyHistoryDialog, listbox);
-  gtk_widget_class_bind_template_child (widget_class, EphyHistoryDialog, popup_menu);
 
-  gtk_widget_class_bind_template_callback (widget_class, on_listbox_row_activated);
-  gtk_widget_class_bind_template_callback (widget_class, on_listbox_row_selected);
-  gtk_widget_class_bind_template_callback (widget_class, on_listbox_button_press_event);
   gtk_widget_class_bind_template_callback (widget_class, on_listbox_key_press_event);
+  gtk_widget_class_bind_template_callback (widget_class, on_listbox_row_activated);
   gtk_widget_class_bind_template_callback (widget_class, on_key_press_event);
+  gtk_widget_class_bind_template_callback (widget_class, on_key_release_event);
   gtk_widget_class_bind_template_callback (widget_class, on_search_text_changed);
+  gtk_widget_class_bind_template_callback (widget_class, on_dialog_selection_active_toggled);
+  gtk_widget_class_bind_template_callback (widget_class, on_dialog_selection_delete_clicked);
   gtk_widget_class_bind_template_callback (widget_class, on_edge_reached);
 }
 
@@ -718,13 +780,23 @@ ephy_history_dialog_new (EphyHistoryService *history_service)
   return GTK_WIDGET (self);
 }
 
+static void
+add_open_selection_button (EphyHistoryDialog *self)
+{
+  GtkWidget *button = gtk_button_new ();
+
+  gtk_widget_set_visible (button, TRUE);
+  gtk_widget_set_sensitive (button, FALSE);
+  gtk_button_set_use_underline (GTK_BUTTON (button), TRUE);
+  gtk_button_set_label (GTK_BUTTON (button), "_Open");
+  g_signal_connect (button, "clicked", (GCallback)open_checked_rows, self);
+  ephy_data_dialog_add_selection_action (EPHY_DATA_DIALOG (self), button);
+}
+
 static GActionGroup *
 create_action_group (EphyHistoryDialog *self)
 {
   const GActionEntry entries[] = {
-    { "open-selection", open_selection },
-    { "copy-url", copy_url },
-    { "forget", forget },
     { "forget-all", forget_all }
   };
   GSimpleActionGroup *group;
@@ -752,7 +824,7 @@ ephy_history_dialog_init (EphyHistoryDialog *self)
 
   ephy_gui_ensure_window_group (GTK_WINDOW (self));
 
-  gtk_menu_attach_to_widget (GTK_MENU (self->popup_menu), GTK_WIDGET (self), NULL);
+  add_open_selection_button (self);
 
   self->action_group = create_action_group (self);
   gtk_widget_insert_action_group (GTK_WIDGET (self), "history", self->action_group);
@@ -763,8 +835,6 @@ ephy_history_dialog_init (EphyHistoryDialog *self)
 
     action = g_action_map_lookup_action (G_ACTION_MAP (self->action_group), "forget-all");
     g_simple_action_set_enabled (G_SIMPLE_ACTION (action), FALSE);
-
-    update_selection_actions (self->action_group, FALSE);
   } else {
     ephy_data_dialog_set_can_clear (EPHY_DATA_DIALOG (self), TRUE);
   }
diff --git a/src/preferences/ephy-data-dialog.c b/src/preferences/ephy-data-dialog.c
index bd22a44d5..4ebe801e5 100644
--- a/src/preferences/ephy-data-dialog.c
+++ b/src/preferences/ephy-data-dialog.c
@@ -26,20 +26,29 @@
 
 typedef struct {
   GtkWidget *box;
+  GtkWidget *header_bars_stack;
+  GtkWidget *window_header_bar;
+  GtkWidget *selection_header_bar;
   GtkWidget *child;
   GtkWidget *clear_all_button;
   GtkWidget *search_bar;
   GtkWidget *search_entry;
   GtkWidget *search_button;
-  GtkWidget *stack;
+  GtkWidget *data_presentation_stack;
   GtkWidget *empty_title_label;
   GtkWidget *empty_description_label;
   GtkWidget *spinner;
+  GtkWidget *action_bars_stack;
+  GtkWidget *regular_action_bar;
+  GtkWidget *selection_action_bar;
+  GtkWidget *selection_actions_box;
+  GtkWidget *selection_delete_button;
 
   gboolean is_loading : 1;
   gboolean has_data : 1;
   gboolean has_search_results : 1;
   gboolean can_clear : 1;
+  gboolean selection_active : 1;
   char *search_text;
 } EphyDataDialogPrivate;
 
@@ -58,6 +67,7 @@ enum {
   PROP_HAS_DATA,
   PROP_HAS_SEARCH_RESULTS,
   PROP_CAN_CLEAR,
+  PROP_SELECTION_ACTIVE,
   LAST_PROP,
 };
 
@@ -65,6 +75,7 @@ static GParamSpec *obj_properties[LAST_PROP];
 
 enum {
   CLEAR_ALL_CLICKED,
+  SELECTION_DELETE_CLICKED,
   LAST_SIGNAL,
 };
 
@@ -74,26 +85,33 @@ static void
 update (EphyDataDialog *self)
 {
   EphyDataDialogPrivate *priv = ephy_data_dialog_get_instance_private (self);
+  GtkStack *data_stack = GTK_STACK (priv->data_presentation_stack);
+  GtkStack *action_bars_stack = GTK_STACK (priv->action_bars_stack);
   gboolean has_data = priv->has_data && priv->child && gtk_widget_get_visible (priv->child);
 
   if (priv->is_loading) {
-    gtk_stack_set_visible_child_name (GTK_STACK (priv->stack), "loading");
+    gtk_stack_set_visible_child_name (data_stack, "loading");
     gtk_spinner_start (GTK_SPINNER (priv->spinner));
   } else {
     if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (priv->search_button))) {
       if (has_data && priv->has_search_results)
-        gtk_stack_set_visible_child (GTK_STACK (priv->stack), priv->child);
+        gtk_stack_set_visible_child (data_stack, priv->child);
       else
-        gtk_stack_set_visible_child_name (GTK_STACK (priv->stack), "no-results");
+        gtk_stack_set_visible_child_name (data_stack, "no-results");
     } else {
       if (has_data)
-        gtk_stack_set_visible_child (GTK_STACK (priv->stack), priv->child);
+        gtk_stack_set_visible_child (data_stack, priv->child);
       else
-        gtk_stack_set_visible_child_name (GTK_STACK (priv->stack), "empty");
+        gtk_stack_set_visible_child_name (data_stack, "empty");
     }
     gtk_spinner_stop (GTK_SPINNER (priv->spinner));
   }
 
+  if (priv->selection_active)
+    gtk_stack_set_visible_child (action_bars_stack, priv->selection_action_bar);
+  else
+    gtk_stack_set_visible_child (action_bars_stack, priv->regular_action_bar);
+
   gtk_widget_set_sensitive (priv->clear_all_button, has_data && priv->can_clear);
   gtk_widget_set_sensitive (priv->search_button, has_data);
 }
@@ -104,6 +122,35 @@ on_clear_all_button_clicked (EphyDataDialog *self)
   g_signal_emit (self, signals[CLEAR_ALL_CLICKED], 0);
 }
 
+static void
+on_selection_button_clicked (GtkButton      *button,
+                             EphyDataDialog *self)
+{
+  EphyDataDialogPrivate *priv = ephy_data_dialog_get_instance_private (self);
+  GtkStack *header_bars_stack = GTK_STACK (priv->header_bars_stack);
+
+  ephy_data_dialog_set_selection_active (self, TRUE);
+  gtk_stack_set_visible_child (header_bars_stack, priv->selection_header_bar);
+}
+
+static void
+on_selection_cancel_button_clicked (GtkButton      *button,
+                                    EphyDataDialog *self)
+{
+  EphyDataDialogPrivate *priv = ephy_data_dialog_get_instance_private (self);
+  GtkStack *header_bars_stack = GTK_STACK (priv->header_bars_stack);
+
+  ephy_data_dialog_set_selection_active (self, FALSE);
+  gtk_stack_set_visible_child (header_bars_stack, priv->window_header_bar);
+}
+
+static void
+on_selection_delete_button_clicked (GtkButton      *button,
+                                    EphyDataDialog *self)
+{
+  g_signal_emit (self, signals[SELECTION_DELETE_CLICKED], 0);
+}
+
 static void
 on_search_entry_changed (GtkSearchEntry *entry,
                          EphyDataDialog *self)
@@ -184,6 +231,9 @@ ephy_data_dialog_set_property (GObject      *object,
     case PROP_CAN_CLEAR:
       ephy_data_dialog_set_can_clear (self, g_value_get_boolean (value));
       break;
+    case PROP_SELECTION_ACTIVE:
+      ephy_data_dialog_set_selection_active (self, g_value_get_boolean (value));
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -233,6 +283,9 @@ ephy_data_dialog_get_property (GObject    *object,
     case PROP_CAN_CLEAR:
       g_value_set_boolean (value, ephy_data_dialog_get_can_clear (self));
       break;
+    case PROP_SELECTION_ACTIVE:
+      g_value_set_boolean (value, ephy_data_dialog_get_selection_active (self));
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -256,6 +309,7 @@ ephy_data_dialog_add (GtkContainer *container,
 {
   EphyDataDialog *self = EPHY_DATA_DIALOG (container);
   EphyDataDialogPrivate *priv = ephy_data_dialog_get_instance_private (self);
+  GtkStack *data_stack = GTK_STACK (priv->data_presentation_stack);
 
   if (!priv->box) {
     GTK_CONTAINER_CLASS (ephy_data_dialog_parent_class)->add (container, child);
@@ -265,7 +319,7 @@ ephy_data_dialog_add (GtkContainer *container,
   g_assert (!priv->child);
 
   priv->child = child;
-  gtk_container_add (GTK_CONTAINER (priv->stack), child);
+  gtk_container_add (GTK_CONTAINER (data_stack), child);
 
   update (self);
 }
@@ -360,8 +414,24 @@ ephy_data_dialog_class_init (EphyDataDialogClass *klass)
                           FALSE,
                           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
 
+  obj_properties[PROP_SELECTION_ACTIVE] =
+    g_param_spec_boolean ("selection-active",
+                          "Selection active",
+                          "Is selection active?",
+                          FALSE,
+                          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
   g_object_class_install_properties (object_class, LAST_PROP, obj_properties);
 
+  signals[SELECTION_DELETE_CLICKED] =
+    g_signal_new ("selection-delete-clicked",
+                  G_OBJECT_CLASS_TYPE (klass),
+                  G_SIGNAL_RUN_FIRST | G_SIGNAL_RUN_LAST,
+                  0, NULL, NULL, NULL,
+                  G_TYPE_NONE,
+                  0,
+                  G_TYPE_NONE);
+
   /**
    * EphyLocationEntry::user-changed:
    * @entry: the object on which the signal is emitted
@@ -381,6 +451,9 @@ ephy_data_dialog_class_init (EphyDataDialogClass *klass)
   gtk_widget_class_set_template_from_resource (widget_class,
                                                "/org/gnome/epiphany/gtk/data-dialog.ui");
   gtk_widget_class_bind_template_child_private (widget_class, EphyDataDialog, box);
+  gtk_widget_class_bind_template_child_private (widget_class, EphyDataDialog, header_bars_stack);
+  gtk_widget_class_bind_template_child_private (widget_class, EphyDataDialog, window_header_bar);
+  gtk_widget_class_bind_template_child_private (widget_class, EphyDataDialog, selection_header_bar);
   gtk_widget_class_bind_template_child_private (widget_class, EphyDataDialog, clear_all_button);
   gtk_widget_class_bind_template_child_private (widget_class, EphyDataDialog, empty_title_label);
   gtk_widget_class_bind_template_child_private (widget_class, EphyDataDialog, empty_description_label);
@@ -388,10 +461,18 @@ ephy_data_dialog_class_init (EphyDataDialogClass *klass)
   gtk_widget_class_bind_template_child_private (widget_class, EphyDataDialog, search_button);
   gtk_widget_class_bind_template_child_private (widget_class, EphyDataDialog, search_entry);
   gtk_widget_class_bind_template_child_private (widget_class, EphyDataDialog, spinner);
-  gtk_widget_class_bind_template_child_private (widget_class, EphyDataDialog, stack);
+  gtk_widget_class_bind_template_child_private (widget_class, EphyDataDialog, data_presentation_stack);
+  gtk_widget_class_bind_template_child_private (widget_class, EphyDataDialog, action_bars_stack);
+  gtk_widget_class_bind_template_child_private (widget_class, EphyDataDialog, regular_action_bar);
+  gtk_widget_class_bind_template_child_private (widget_class, EphyDataDialog, selection_action_bar);
+  gtk_widget_class_bind_template_child_private (widget_class, EphyDataDialog, selection_actions_box);
+  gtk_widget_class_bind_template_child_private (widget_class, EphyDataDialog, selection_delete_button);
 
   gtk_widget_class_bind_template_callback (widget_class, on_key_press_event);
   gtk_widget_class_bind_template_callback (widget_class, on_clear_all_button_clicked);
+  gtk_widget_class_bind_template_callback (widget_class, on_selection_button_clicked);
+  gtk_widget_class_bind_template_callback (widget_class, on_selection_cancel_button_clicked);
+  gtk_widget_class_bind_template_callback (widget_class, on_selection_delete_button_clicked);
   gtk_widget_class_bind_template_callback (widget_class, on_search_entry_changed);
 }
 
@@ -569,6 +650,69 @@ ephy_data_dialog_set_can_clear (EphyDataDialog *self,
   g_object_notify_by_pspec (G_OBJECT (self), obj_properties[PROP_CAN_CLEAR]);
 }
 
+gboolean
+ephy_data_dialog_get_selection_active (EphyDataDialog *self)
+{
+  EphyDataDialogPrivate *priv;
+
+  g_assert (EPHY_IS_DATA_DIALOG (self));
+
+  priv = ephy_data_dialog_get_instance_private (self);
+
+  return priv->selection_active;
+}
+
+void
+ephy_data_dialog_set_selection_active (EphyDataDialog *self,
+                                       gboolean        selection_active)
+{
+  EphyDataDialogPrivate *priv;
+
+  g_assert (EPHY_IS_DATA_DIALOG (self));
+
+  priv = ephy_data_dialog_get_instance_private (self);
+
+  if (priv->selection_active == selection_active)
+    return;
+
+  priv->selection_active = selection_active;
+
+  update (self);
+
+  g_object_notify_by_pspec (G_OBJECT (self), obj_properties[PROP_SELECTION_ACTIVE]);
+}
+
+void
+ephy_data_dialog_set_selection_actions_sensitive (EphyDataDialog *self,
+                                                  gboolean        actions_sensitive)
+{
+  EphyDataDialogPrivate *priv;
+  GtkContainer *selection_actions_box;
+  g_autoptr (GList) selection_actions_widgets = NULL;
+  GList *iter;
+
+  g_assert (EPHY_IS_DATA_DIALOG (self));
+
+  priv = ephy_data_dialog_get_instance_private (self);
+  selection_actions_box = GTK_CONTAINER (priv->selection_actions_box);
+  selection_actions_widgets = gtk_container_get_children (selection_actions_box);
+
+  for (iter = selection_actions_widgets; iter != NULL; iter = g_list_next (iter)) {
+    GtkWidget *action_widget = iter->data;
+
+    gtk_widget_set_sensitive (action_widget, actions_sensitive);
+  }
+}
+
+void
+ephy_data_dialog_add_selection_action (EphyDataDialog *self,
+                                       GtkWidget      *action_widget)
+{
+  EphyDataDialogPrivate *priv = ephy_data_dialog_get_instance_private (self);
+
+  gtk_container_add (GTK_CONTAINER (priv->selection_actions_box), action_widget);
+}
+
 const gchar *
 ephy_data_dialog_get_search_text (EphyDataDialog *self)
 {
diff --git a/src/preferences/ephy-data-dialog.h b/src/preferences/ephy-data-dialog.h
index ed89d6851..e0d0a3bf2 100644
--- a/src/preferences/ephy-data-dialog.h
+++ b/src/preferences/ephy-data-dialog.h
@@ -54,6 +54,16 @@ gboolean ephy_data_dialog_get_can_clear (EphyDataDialog *self);
 void     ephy_data_dialog_set_can_clear (EphyDataDialog *self,
                                          gboolean        can_clear);
 
+gboolean ephy_data_dialog_get_selection_active (EphyDataDialog *self);
+void     ephy_data_dialog_set_selection_active (EphyDataDialog *self,
+                                                gboolean        selection_active);
+
+void ephy_data_dialog_set_selection_actions_sensitive (EphyDataDialog *self,
+                                                       gboolean        actions_sensitive);
+
+void ephy_data_dialog_add_selection_action (EphyDataDialog *self,
+                                            GtkWidget      *action_bar_widget);
+
 const gchar *ephy_data_dialog_get_search_text (EphyDataDialog *self);
 
 G_END_DECLS
diff --git a/src/resources/gtk/data-dialog.ui b/src/resources/gtk/data-dialog.ui
index c8cf048ea..7503054df 100644
--- a/src/resources/gtk/data-dialog.ui
+++ b/src/resources/gtk/data-dialog.ui
@@ -3,11 +3,6 @@
 <interface>
   <requires lib="gtk+" version="3.20"/>
   <requires lib="libhandy" version="0.0"/>
-  <object class="GtkImage" id="search_image">
-    <property name="visible">True</property>
-    <property name="can_focus">False</property>
-    <property name="icon_name">edit-find-symbolic</property>
-  </object>
   <object class="GtkImage" id="clear_all_image">
     <property name="visible">True</property>
     <property name="can_focus">False</property>
@@ -28,47 +23,113 @@
         <property name="can_focus">False</property>
         <property name="orientation">vertical</property>
         <child>
-          <object class="HdyHeaderBar">
+          <object class="GtkStack" id="header_bars_stack">
             <property name="visible">True</property>
-            <property name="can_focus">False</property>
-            <property name="show_close_button">True</property>
-            <property name="title" bind-source="EphyDataDialog" bind-property="title"/>
+            <property name="transition-type">GTK_STACK_TRANSITION_TYPE_CROSSFADE</property>
             <child>
-              <object class="GtkButton" id="clear_all_button">
+              <object class="HdyHeaderBar" id="window_header_bar">
                 <property name="visible">True</property>
-                <property name="can_focus">True</property>
-                <property name="receives_default">True</property>
-                <property name="image">clear_all_image</property>
-                <property name="always_show_image">True</property>
-                <signal name="clicked" handler="on_clear_all_button_clicked" swapped="yes"/>
-                <accelerator key="Delete" modifiers="GDK_SHIFT_MASK" signal="clicked"/>
-                <style>
-                  <class name="destructive-action"/>
-                  <class name="image-button"/>
-                </style>
+                <property name="can_focus">False</property>
+                <property name="show_close_button">True</property>
+                <property name="title" bind-source="EphyDataDialog" bind-property="title"/>
+                <child>
+                  <object class="GtkButton" id="selection_button">
+                    <property name="visible">True</property>
+                    <property name="valign">center</property>
+                    <property name="use-underline">True</property>
+                    <signal name="clicked" handler="on_selection_button_clicked"/>
+                    <style>
+                      <class name="image-button"/>
+                    </style>
+                    <child internal-child="accessible">
+                      <object class="AtkObject" id="a11y-button3">
+                        <property name="accessible-name" translatable="yes">Select Items</property>
+                      </object>
+                    </child>
+                    <child>
+                      <object class="GtkImage">
+                        <property name="visible">True</property>
+                        <property name="icon-name">object-select-symbolic</property>
+                        <property name="icon-size">1</property>
+                      </object>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="pack-type">end</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkToggleButton" id="search_button">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="receives_default">True</property>
+                    <property name="always_show_image">True</property>
+                    <property name="active" bind-source="search_bar" bind-property="search-mode-enabled" 
bind-flags="sync-create|bidirectional"/>
+                    <accelerator key="F" modifiers="GDK_CONTROL_MASK" signal="clicked"/>
+                    <child>
+                      <object class="GtkImage">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="icon_name">edit-find-symbolic</property>
+                      </object>
+                    </child>
+                    <child internal-child="accessible">
+                      <object class="AtkObject">
+                        <property name="AtkObject::accessible-name" translatable="yes">Search</property>
+                      </object>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="pack_type">end</property>
+                  </packing>
+                </child>
               </object>
             </child>
             <child>
-              <object class="GtkToggleButton" id="search_button">
+              <object class="HdyHeaderBar" id="selection_header_bar">
                 <property name="visible">True</property>
-                <property name="can_focus">True</property>
-                <property name="receives_default">True</property>
-                <property name="image">search_image</property>
-                <property name="always_show_image">True</property>
-                <property name="active" bind-source="search_bar" bind-property="search-mode-enabled" 
bind-flags="sync-create|bidirectional"/>
-                <accelerator key="F" modifiers="GDK_CONTROL_MASK" signal="clicked"/>
+                <property name="title" bind-source="EphyDataDialog" bind-property="title"/>
                 <style>
-                  <class name="image-button"/>
+                  <class name="selection-mode"/>
                 </style>
-                <child internal-child="accessible">
-                  <object class="AtkObject">
-                    <property name="AtkObject::accessible-name" translatable="yes">Search</property>
+                <child>
+                  <object class="GtkButton" id="selection_cancel_button">
+                    <property name="visible">True</property>
+                    <property name="valign">center</property>
+                    <property name="use-underline">True</property>
+                    <property name="label" translatable="yes">_Cancel</property>
+                    <signal name="clicked" handler="on_selection_cancel_button_clicked"/>
+                    <style>
+                      <class name="text-button"/>
+                    </style>
+                  </object>
+                  <packing>
+                    <property name="pack-type">end</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkToggleButton" id="selection_search_button">
+                    <property name="visible">True</property>
+                    <property name="active" bind-source="search_bar" bind-property="search-mode-enabled" 
bind-flags="sync-create|bidirectional"/>
+                    <accelerator key="F" modifiers="GDK_CONTROL_MASK" signal="clicked"/>
+                    <child>
+                      <object class="GtkImage">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="icon_name">edit-find-symbolic</property>
+                      </object>
+                    </child>
+                    <child internal-child="accessible">
+                      <object class="AtkObject">
+                        <property name="AtkObject::accessible-name" translatable="yes">Search</property>
+                      </object>
+                    </child>
                   </object>
+                  <packing>
+                    <property name="pack_type">end</property>
+                  </packing>
                 </child>
               </object>
-              <packing>
-                <property name="pack_type">end</property>
-              </packing>
             </child>
           </object>
         </child>
@@ -104,7 +165,7 @@
           </packing>
         </child>
         <child>
-          <object class="GtkStack" id="stack">
+          <object class="GtkStack" id="data_presentation_stack">
             <property name="can_focus">False</property>
             <property name="expand">True</property>
             <property name="visible">True</property>
@@ -264,6 +325,57 @@
             <property name="expand">True</property>
           </packing>
         </child>
+        <child>
+          <object class="GtkStack" id="action_bars_stack">
+            <property name="visible">True</property>
+            <property name="transition-type">GTK_STACK_TRANSITION_TYPE_CROSSFADE</property>
+            <child>
+              <object class="GtkActionBar" id="regular_action_bar">
+                <property name="visible">True</property>
+                <child>
+                  <object class="GtkButton" id="clear_all_button">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="receives_default">True</property>
+                    <property name="use-underline">True</property>
+                    <property name="label" translatable="yes">_Clear All</property>
+                    <signal name="clicked" handler="on_clear_all_button_clicked" swapped="yes"/>
+                    <accelerator key="Delete" modifiers="GDK_SHIFT_MASK" signal="clicked"/>
+                    <style>
+                      <class name="destructive-action"/>
+                      <class name="image-button"/>
+                    </style>
+                  </object>
+                </child>
+              </object>
+            </child>
+            <child>
+              <object class="GtkActionBar" id="selection_action_bar">
+                <property name="visible">True</property>
+                <child>
+                  <object class="GtkBox" id="selection_actions_box">
+                    <property name="visible">True</property>
+                    <property name="spacing">5</property>
+                    <child>
+                      <object class="GtkButton" id="selection_delete_button">
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                        <property name="receives_default">True</property>
+                        <property name="sensitive">False</property>
+                        <property name="use_underline">True</property>
+                        <property name="label" translatable="yes">_Delete</property>
+                        <signal name="clicked" handler="on_selection_delete_button_clicked"/>
+                        <style>
+                          <class name="destructive-action"/>
+                        </style>
+                      </object>
+                    </child>
+                  </object>
+                </child>
+              </object>
+            </child>
+          </object>
+        </child>
       </object>
     </child>
   </template>
diff --git a/src/resources/gtk/history-dialog.ui b/src/resources/gtk/history-dialog.ui
index 63375ddb5..03d90dfce 100644
--- a/src/resources/gtk/history-dialog.ui
+++ b/src/resources/gtk/history-dialog.ui
@@ -10,8 +10,13 @@
     <property name="search_description" translatable="yes">Search history</property>
     <property name="empty_title" translatable="yes">The History is Empty</property>
     <property name="empty_description" translatable="yes">Visited pages will be listed here</property>
+    <property name="default-width">640</property>
+    <property name="default-height">800</property>
     <signal name="key-press-event" handler="on_key_press_event" swapped="no"/>
+    <signal name="key-release-event" handler="on_key_release_event"/>
+    <signal name="selection-delete-clicked" handler="on_dialog_selection_delete_clicked"/>
     <signal name="notify::search-text" handler="on_search_text_changed" swapped="yes"/>
+    <signal name="notify::selection-active" handler="on_dialog_selection_active_toggled"/>
     <child>
       <object class="GtkScrolledWindow" id="scrolled_window">
         <property name="visible">True</property>
@@ -30,12 +35,10 @@
                 <property name="margin_top">6</property>
                 <property name="margin_bottom">6</property>
                 <property name="valign">start</property>
-                <property name="selection_mode">multiple</property>
-                <property name="activate_on_single_click">False</property>
-                <signal name="button-press-event" handler="on_listbox_button_press_event" swapped="no"/>
+                <property name="selection_mode">none</property>
+                <property name="activate_on_single_click">True</property>
                 <signal name="key-press-event" handler="on_listbox_key_press_event" swapped="no"/>
-                <signal name="row-activated" handler="on_listbox_row_activated" swapped="no"/>
-                <signal name="row-selected" handler="on_listbox_row_selected"/>
+                <signal name="row-activated" handler="on_listbox_row_activated"/>
                 <style>
                   <class name="content"/>
                 </style>
@@ -46,35 +49,4 @@
       </object>
     </child>
   </template>
-  <object class="GtkMenu" id="popup_menu">
-    <property name="visible">True</property>
-    <property name="can_focus">False</property>
-    <child>
-      <object class="GtkMenuItem">
-        <property name="visible">True</property>
-        <property name="can_focus">True</property>
-        <property name="action_name">history.open-selection</property>
-        <property name="label" translatable="yes">_Open</property>
-        <property name="use_underline">True</property>
-      </object>
-    </child>
-    <child>
-      <object class="GtkMenuItem">
-        <property name="visible">True</property>
-        <property name="can_focus">True</property>
-        <property name="action_name">history.copy-url</property>
-        <property name="label" translatable="yes">_Copy Location</property>
-        <property name="use_underline">True</property>
-      </object>
-    </child>
-    <child>
-      <object class="GtkMenuItem">
-        <property name="visible">True</property>
-        <property name="can_focus">True</property>
-        <property name="action_name">history.forget</property>
-        <property name="label" translatable="yes">_Delete</property>
-        <property name="use_underline">True</property>
-      </object>
-    </child>
-  </object>
 </interface>


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