[epiphany] ephy-history-dialog: Change history dialog to be responsive



commit 8857fb05038bf25266eb442acb1d1fbf541cc1ea
Author: Jan-Michael Brummer <jan brummer tabos org>
Date:   Sun Dec 9 21:58:19 2018 +0100

    ephy-history-dialog: Change history dialog to be responsive

 src/ephy-history-dialog.c           | 621 ++++++++++++++++++------------------
 src/ephy-history-dialog.h           |   2 +-
 src/resources/gtk/history-dialog.ui | 308 ++++++++----------
 3 files changed, 430 insertions(+), 501 deletions(-)
---
diff --git a/src/ephy-history-dialog.c b/src/ephy-history-dialog.c
index 6c81ecc6b..c3d4e8b2a 100644
--- a/src/ephy-history-dialog.c
+++ b/src/ephy-history-dialog.c
@@ -3,6 +3,7 @@
  *  Copyright © 2003, 2004 Marco Pesenti Gritti <mpeseng tin it>
  *  Copyright © 2003, 2004 Christian Persch
  *  Copyright © 2012 Igalia S.L
+ *  Copyright © 2018 Jan-Michael Brummer
  *
  *  This file is part of Epiphany.
  *
@@ -38,29 +39,18 @@
 #include <string.h>
 #include <time.h>
 
-#define NUM_RESULTS_LIMIT -1
-/* 3/5 of gdkframeclockidle.c's FRAME_INTERVAL (16667 microsecs) */
-#define GTK_TREE_VIEW_TIME_MS_PER_IDLE 10
+#define NUM_FETCH_LIMIT 15
 
 struct _EphyHistoryDialog {
-  GtkDialog parent_instance;
+  GtkWindow parent_instance;
 
   EphySnapshotService *snapshot_service;
   EphyHistoryService *history_service;
   GCancellable *cancellable;
 
-  GtkWidget *treeview;
-  GtkTreeSelection *tree_selection;
-  GtkWidget *liststore;
-  GtkTreeViewColumn *date_column;
-  GtkTreeViewColumn *name_column;
-  GtkTreeViewColumn *location_column;
-  GtkWidget *date_renderer;
-  GtkWidget *location_renderer;
-  GMenuModel *treeview_popup_menu_model;
-
+  GtkWidget *listbox;
   GtkWidget *forget_all_button;
-  GtkWidget *forget_button;
+  GtkWidget *popover_menu;
 
   GActionGroup *action_group;
 
@@ -69,13 +59,12 @@ struct _EphyHistoryDialog {
 
   char *search_text;
 
-  gboolean sort_ascending;
-  gint sort_column;
+  gint num_fetch;
 
   GtkWidget *confirmation_dialog;
 };
 
-G_DEFINE_TYPE (EphyHistoryDialog, ephy_history_dialog, GTK_TYPE_DIALOG)
+G_DEFINE_TYPE (EphyHistoryDialog, ephy_history_dialog, GTK_TYPE_WINDOW)
 
 enum {
   PROP_0,
@@ -85,47 +74,30 @@ enum {
 
 static GParamSpec *obj_properties[LAST_PROP];
 
-typedef enum {
-  COLUMN_DATE,
-  COLUMN_NAME,
-  COLUMN_LOCATION,
-  COLUMN_SYNC_ID
-} EphyHistoryDialogColumns;
+static gboolean add_urls_source (EphyHistoryDialog *self);
 
-static gboolean
-add_urls_source (EphyHistoryDialog *self)
+static EphyHistoryURL *
+get_url_from_row (GtkListBoxRow *row)
 {
-  EphyHistoryURL *url;
-  GTimer *timer;
-  GList *element;
+  return ephy_history_url_new (g_object_get_data (G_OBJECT (row), "url"),
+                               g_object_get_data (G_OBJECT (row), "title"),
+                               0,
+                               0,
+                               0);
+}
 
-  if (self->urls == NULL) {
-    self->sorter_source = 0;
-    return G_SOURCE_REMOVE;
-  }
+static void
+clear_listbox (GtkWidget *listbox)
+{
+  GList *children, *iter;
 
-  timer = g_timer_new ();
-  g_timer_start (timer);
-
-  do {
-    element = self->urls;
-    url = element->data;
-    gtk_list_store_insert_with_values (GTK_LIST_STORE (self->liststore),
-                                       NULL, G_MAXINT,
-                                       COLUMN_DATE, url->last_visit_time,
-                                       COLUMN_NAME, url->title,
-                                       COLUMN_LOCATION, url->url,
-                                       COLUMN_SYNC_ID, url->sync_id,
-                                       -1);
-    self->urls = g_list_remove_link (self->urls, element);
-    ephy_history_url_free (url);
-    g_list_free_1 (element);
-  } while (self->urls &&
-           g_timer_elapsed (timer, NULL) < GTK_TREE_VIEW_TIME_MS_PER_IDLE / 1000.);
-
-  g_timer_destroy (timer);
+  children = gtk_container_get_children (GTK_CONTAINER (listbox));
 
-  return G_SOURCE_CONTINUE;
+  for (iter = children; iter != NULL; iter = g_list_next (iter)) {
+    gtk_widget_destroy (GTK_WIDGET (iter->data));
+  }
+
+  g_list_free (children);
 }
 
 static void
@@ -135,21 +107,16 @@ on_find_urls_cb (gpointer service,
                  gpointer user_data)
 {
   EphyHistoryDialog *self = EPHY_HISTORY_DIALOG (user_data);
-  GtkTreeViewColumn *column;
 
   if (success != TRUE)
     return;
 
-  self->urls = (GList *)result_data;
 
-  gtk_tree_view_set_model (GTK_TREE_VIEW (self->treeview), NULL);
-  gtk_list_store_clear (GTK_LIST_STORE (self->liststore));
-  gtk_tree_view_set_model (GTK_TREE_VIEW (self->treeview), GTK_TREE_MODEL (self->liststore));
+  self->urls = (GList *)result_data;
 
-  column = gtk_tree_view_get_column (GTK_TREE_VIEW (self->treeview), self->sort_column);
-  gtk_tree_view_column_set_sort_order (column, self->sort_ascending ? GTK_SORT_ASCENDING : 
GTK_SORT_DESCENDING);
-  gtk_tree_view_column_set_sort_indicator (column, TRUE);
+  clear_listbox (self->listbox);
 
+  self->num_fetch = NUM_FETCH_LIMIT;
   self->sorter_source = g_idle_add ((GSourceFunc)add_urls_source, self);
 }
 
@@ -167,21 +134,22 @@ substrings_filter (EphyHistoryDialog *self)
   while (*p) {
     substrings = g_list_prepend (substrings, *p++);
   }
-  ;
+
   g_free (tokens);
 
   return substrings;
 }
 
 static void
-remove_pending_sorter_source (EphyHistoryDialog *self)
+remove_pending_sorter_source (EphyHistoryDialog *self,
+                              gboolean           free_urls)
 {
   if (self->sorter_source != 0) {
     g_source_remove (self->sorter_source);
     self->sorter_source = 0;
   }
 
-  if (self->urls != NULL) {
+  if (free_urls && self->urls != NULL) {
     g_list_free_full (self->urls, (GDestroyNotify)ephy_history_url_free);
     self->urls = NULL;
   }
@@ -193,36 +161,195 @@ filter_now (EphyHistoryDialog *self)
   gint64 from, to;
   GList *substrings;
   EphyHistorySortType type;
-
   substrings = substrings_filter (self);
 
   from = to = -1;       /* all */
 
-  switch (self->sort_column) {
-    case COLUMN_DATE:
-      type = self->sort_ascending ? EPHY_HISTORY_SORT_LEAST_RECENTLY_VISITED : 
EPHY_HISTORY_SORT_MOST_RECENTLY_VISITED;
-      break;
-    case COLUMN_NAME:
-      type = self->sort_ascending ? EPHY_HISTORY_SORT_TITLE_ASCENDING : EPHY_HISTORY_SORT_TITLE_DESCENDING;
-      break;
-    case COLUMN_LOCATION:
-      type = self->sort_ascending ? EPHY_HISTORY_SORT_URL_ASCENDING : EPHY_HISTORY_SORT_URL_DESCENDING;
-      break;
-    default:
-      type = EPHY_HISTORY_SORT_MOST_RECENTLY_VISITED;
-  }
+  type = EPHY_HISTORY_SORT_MOST_RECENTLY_VISITED;
 
-  remove_pending_sorter_source (self);
+  remove_pending_sorter_source (self, TRUE);
 
   ephy_history_service_find_urls (self->history_service,
                                   from, to,
-                                  NUM_RESULTS_LIMIT, 0,
+                                  -1, 0,
                                   substrings,
                                   type,
                                   self->cancellable,
                                   (EphyHistoryJobCallback)on_find_urls_cb, self);
 }
 
+static GList *
+get_selection (EphyHistoryDialog *self)
+{
+  GList *selected_rows = gtk_list_box_get_selected_rows (GTK_LIST_BOX (self->listbox));
+  GList *list = NULL;
+  GList *tmp;
+
+  for (tmp = selected_rows; tmp != NULL; tmp = tmp->next) {
+    EphyHistoryURL *url = get_url_from_row (tmp->data);
+
+    if (url == NULL) {
+      continue;
+    }
+
+    list = g_list_append (list, url);
+  }
+
+  return g_list_reverse (list);
+}
+
+static void
+on_browse_history_deleted_cb (gpointer service,
+                              gboolean success,
+                              gpointer result_data,
+                              gpointer user_data)
+{
+  EphyHistoryDialog *self = EPHY_HISTORY_DIALOG (user_data);
+
+  if (success != TRUE)
+    return;
+
+  filter_now (self);
+}
+
+static void
+delete_selected (EphyHistoryDialog *self)
+{
+  GList *selected;
+
+  selected = get_selection (self);
+  ephy_history_service_delete_urls (self->history_service, selected, 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);
+}
+
+static void
+forget_clicked (GtkButton *button,
+                gpointer   user_data)
+{
+  EphyHistoryDialog *self = EPHY_HISTORY_DIALOG (user_data);
+  GtkListBoxRow *row = g_object_get_data (G_OBJECT (button), "row");
+
+  gtk_list_box_select_row (GTK_LIST_BOX (self->listbox), row);
+
+  delete_selected (self);
+}
+
+static GtkWidget *
+create_row (EphyHistoryDialog *self,
+            EphyHistoryURL    *url)
+{
+  EphyEmbedShell *shell = ephy_embed_shell_get_default ();
+  GtkWidget *grid;
+  GtkWidget *title;
+  GtkWidget *address;
+  GtkWidget *date;
+  GtkWidget *row;
+  GtkWidget *separator;
+  GtkWidget *button;
+  PangoAttrList *attrlist;
+  PangoAttribute *attr;
+
+  /* Row */
+  row = gtk_list_box_row_new ();
+  g_object_set_data (G_OBJECT (row), "title", g_strdup (url->title));
+  g_object_set_data (G_OBJECT (row), "url", g_strdup (url->url));
+
+  /* Grid */
+  grid = gtk_grid_new ();
+  gtk_widget_set_margin_start (grid, 6);
+  gtk_widget_set_margin_end (grid, 6);
+  gtk_widget_set_margin_top (grid, 6);
+  gtk_widget_set_margin_bottom (grid, 6);
+  gtk_grid_set_column_spacing (GTK_GRID(grid), 12);
+  gtk_grid_set_row_spacing (GTK_GRID(grid), 6);
+  gtk_widget_set_tooltip_text (grid, url->url);
+
+  /* Title */
+  title = gtk_label_new (url->title);
+  gtk_label_set_ellipsize (GTK_LABEL(title), PANGO_ELLIPSIZE_END);
+  gtk_widget_set_hexpand (title, TRUE);
+  gtk_label_set_xalign (GTK_LABEL(title), 0);
+
+  attrlist = pango_attr_list_new ();
+  attr = pango_attr_weight_new (PANGO_WEIGHT_SEMIBOLD);
+  pango_attr_list_insert (attrlist, attr);
+  gtk_label_set_attributes (GTK_LABEL (title), attrlist);
+  pango_attr_list_unref (attrlist);
+
+  gtk_grid_attach (GTK_GRID (grid), title, 0, 0, 1, 1);
+
+  /* Address */
+  address = gtk_label_new (url->url);
+  gtk_label_set_ellipsize (GTK_LABEL(address), PANGO_ELLIPSIZE_END);
+  gtk_label_set_xalign (GTK_LABEL(address), 0);
+  gtk_widget_set_sensitive (address, FALSE);
+
+  gtk_grid_attach (GTK_GRID (grid), address, 0, 1, 1, 1);
+
+  /* Date */
+  date = gtk_label_new (ephy_time_helpers_utf_friendly_time (url->last_visit_time / 1000000));
+  gtk_label_set_ellipsize (GTK_LABEL(date), PANGO_ELLIPSIZE_END);
+  gtk_label_set_xalign (GTK_LABEL (date), 0);
+
+  gtk_grid_attach (GTK_GRID (grid), date, 0, 2, 1, 1);
+
+  /* Separator */
+  separator = gtk_separator_new (GTK_ORIENTATION_VERTICAL);
+  gtk_grid_attach (GTK_GRID (grid), separator, 1, 0, 1, 3);
+
+  /* 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);
+  gtk_grid_attach (GTK_GRID (grid), button, 2, 0, 1, 3);
+
+  gtk_widget_set_sensitive (button, ephy_embed_shell_get_mode (shell) != EPHY_EMBED_SHELL_MODE_INCOGNITO);
+
+  gtk_container_add (GTK_CONTAINER (row), grid);
+  gtk_widget_show_all (row);
+
+  return row;
+}
+
+static gboolean
+add_urls_source (EphyHistoryDialog *self)
+{
+  EphyHistoryURL *url;
+  GList *element;
+  GtkWidget *row;
+
+  if (self->urls == NULL || !self->num_fetch) {
+    self->sorter_source = 0;
+    gtk_widget_queue_draw (self->listbox);
+    return G_SOURCE_REMOVE;
+  }
+
+  element = self->urls;
+  url = element->data;
+
+  row = create_row (self, url);
+  gtk_list_box_insert (GTK_LIST_BOX(self->listbox), row, -1);
+
+  self->urls = g_list_remove_link (self->urls, element);
+  ephy_history_url_free (url);
+  g_list_free_1 (element);
+
+  self->num_fetch--;
+
+  if (!self->num_fetch) {
+    self->sorter_source = 0;
+    return G_SOURCE_REMOVE;
+  }
+
+  return G_SOURCE_CONTINUE;
+}
+
 static void
 confirmation_dialog_response_cb (GtkWidget         *dialog,
                                  int                response,
@@ -297,75 +424,6 @@ get_target_window (EphyHistoryDialog *self)
     return GTK_WIDGET (gtk_application_get_active_window (GTK_APPLICATION (ephy_shell_get_default ())));
 }
 
-static void
-on_browse_history_deleted_cb (gpointer service,
-                              gboolean success,
-                              gpointer result_data,
-                              gpointer user_data)
-{
-  EphyHistoryDialog *self = EPHY_HISTORY_DIALOG (user_data);
-
-  if (success != TRUE)
-    return;
-
-  filter_now (self);
-}
-
-static EphyHistoryURL *
-get_url_from_path (GtkTreeModel *model,
-                   GtkTreePath  *path)
-{
-  GtkTreeIter iter;
-
-  EphyHistoryURL *url = ephy_history_url_new (NULL, NULL, 0, 0, 0);
-
-  gtk_tree_model_get_iter (model, &iter, path);
-
-  gtk_tree_model_get (model, &iter,
-                      COLUMN_NAME, &url->title,
-                      COLUMN_LOCATION, &url->url,
-                      COLUMN_SYNC_ID, &url->sync_id,
-                      -1);
-  return url;
-}
-
-static void
-get_selection_foreach (GtkTreeModel *model,
-                       GtkTreePath  *path,
-                       GtkTreeIter  *iter,
-                       gpointer     *data)
-{
-  EphyHistoryURL *url;
-
-  url = get_url_from_path (model, path);
-  *data = g_list_prepend (*data, url);
-}
-
-static GList *
-get_selection (EphyHistoryDialog *self)
-{
-  GList *list = NULL;
-
-  gtk_tree_selection_selected_foreach (self->tree_selection,
-                                       (GtkTreeSelectionForeachFunc)get_selection_foreach,
-                                       &list);
-
-  return g_list_reverse (list);
-}
-
-static void
-delete_selected (EphyHistoryDialog *self)
-{
-  GList *selected;
-
-  selected = get_selection (self);
-  ephy_history_service_delete_urls (self->history_service, selected, 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);
-}
-
 static void
 open_selection (GSimpleAction *action,
                 GVariant      *parameter,
@@ -421,9 +479,9 @@ forget (GSimpleAction *action,
 }
 
 static gboolean
-on_treeview_key_press_event (GtkWidget         *widget,
-                             GdkEventKey       *event,
-                             EphyHistoryDialog *self)
+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);
@@ -445,51 +503,6 @@ update_popup_menu_actions (GActionGroup *action_group,
   g_simple_action_set_enabled (G_SIMPLE_ACTION (copy_url_action), only_one_selected_item);
 }
 
-static gboolean
-on_treeview_button_press_event (GtkWidget         *widget,
-                                GdkEventButton    *event,
-                                EphyHistoryDialog *self)
-{
-  if (event->button == 3) {
-    int n;
-    GtkWidget *menu;
-
-    n = gtk_tree_selection_count_selected_rows (self->tree_selection);
-    if (n <= 0)
-      return FALSE;
-
-    update_popup_menu_actions (self->action_group, (n == 1));
-
-    menu = gtk_menu_new_from_model (self->treeview_popup_menu_model);
-    gtk_menu_attach_to_widget (GTK_MENU (menu), GTK_WIDGET (self), NULL);
-    gtk_menu_popup_at_pointer (GTK_MENU (menu), (GdkEvent *)event);
-    return TRUE;
-  }
-
-  return FALSE;
-}
-
-static void
-on_treeview_row_activated (GtkTreeView       *view,
-                           GtkTreePath       *path,
-                           GtkTreeViewColumn *col,
-                           EphyHistoryDialog *self)
-{
-  EphyWindow *window;
-  EphyHistoryURL *url;
-  EphyEmbed *embed;
-
-  window = EPHY_WINDOW (get_target_window (self));
-  url = get_url_from_path (gtk_tree_view_get_model (view),
-                           path);
-  g_assert (url != NULL);
-
-  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);
-}
-
 static void
 on_search_entry_changed (GtkSearchEntry    *entry,
                          EphyHistoryDialog *self)
@@ -503,19 +516,6 @@ on_search_entry_changed (GtkSearchEntry    *entry,
   filter_now (self);
 }
 
-static gboolean
-on_search_key_press_event (GtkWidget        *widget,
-                          GdkEventKey       *event,
-                          EphyHistoryDialog *self)
-{
-  if (event->keyval == GDK_KEY_Escape) {
-    g_signal_emit_by_name (self, "close", NULL);
-    return GDK_EVENT_STOP;
-  }
-
-  return GDK_EVENT_PROPAGATE;
-}
-
 static void
 update_selection_actions (GActionGroup *action_group,
                           gboolean      has_selection)
@@ -534,35 +534,61 @@ update_selection_actions (GActionGroup *action_group,
 }
 
 static void
-on_treeview_selection_changed (GtkTreeSelection  *selection,
-                               EphyHistoryDialog *self)
+on_listbox_row_selected (GtkListBox        *box,
+                         GtkListBoxRow     *row,
+                         EphyHistoryDialog *self)
 {
-  update_selection_actions (self->action_group,
-                            gtk_tree_selection_count_selected_rows (selection) > 0);
+  update_selection_actions (self->action_group, row != NULL);
 }
 
 static void
-on_treeview_column_clicked_event (GtkTreeViewColumn *column,
-                                  EphyHistoryDialog *self)
+on_listbox_row_activated (GtkListBox        *box,
+                          GtkListBoxRow     *row,
+                          EphyHistoryDialog *self)
 {
-  GtkTreeViewColumn *previous_sortby;
-  gint new_sort_column = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (column), "column"));
+  EphyWindow *window;
+  EphyHistoryURL *url;
+  EphyEmbed *embed;
+
+  window = EPHY_WINDOW (get_target_window (self));
+  url = get_url_from_row (row);
+  g_assert (url != NULL);
+
+  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);
+}
+
+static gboolean
+on_listbox_button_press_event (GtkWidget         *widget,
+                               GdkEventButton    *event,
+                               EphyHistoryDialog *self)
+{
+  if (event->button == 3) {
+    GList *rows = gtk_list_box_get_selected_rows ( GTK_LIST_BOX (self->listbox));
+    int n;
 
-  if (new_sort_column == self->sort_column) {
-    self->sort_ascending = !(self->sort_ascending);
-  } else {
-    previous_sortby = gtk_tree_view_get_column (GTK_TREE_VIEW (self->treeview), self->sort_column);
-    gtk_tree_view_column_set_sort_indicator (previous_sortby, FALSE);
+    n = g_list_length (rows);
 
-    self->sort_column = new_sort_column;
-    self->sort_ascending = self->sort_column == COLUMN_DATE ? FALSE : TRUE;
+    if (n <= 0) {
+      g_list_free (rows);
+
+      return FALSE;
+    }
+
+    update_popup_menu_actions (self->action_group, (n == 1));
+
+    gtk_popover_set_relative_to (GTK_POPOVER (self->popover_menu), GTK_WIDGET (rows->data));
+    gtk_popover_popup (GTK_POPOVER (self->popover_menu));
+
+    return TRUE;
   }
 
-  gtk_tree_view_column_set_sort_order (column, self->sort_ascending ? GTK_SORT_ASCENDING : 
GTK_SORT_DESCENDING);
-  gtk_tree_view_column_set_sort_indicator (column, TRUE);
-  filter_now (self);
+  return FALSE;
 }
 
+
 static gboolean
 on_urls_visited_cb (EphyHistoryService *service,
                     EphyHistoryDialog  *self)
@@ -651,11 +677,46 @@ ephy_history_dialog_dispose (GObject *object)
                                           self);
   g_clear_object (&self->history_service);
 
-  remove_pending_sorter_source (self);
+  remove_pending_sorter_source (self, TRUE);
 
   G_OBJECT_CLASS (ephy_history_dialog_parent_class)->dispose (object);
 }
 
+static void
+box_header_func (GtkListBoxRow *row,
+                 GtkListBoxRow *before,
+                 gpointer       user_data)
+{
+  GtkWidget *current;
+
+  if (!before) {
+    gtk_list_box_row_set_header (row, NULL);
+    return;
+  }
+
+  current = gtk_list_box_row_get_header (row);
+  if (!current) {
+    current = gtk_separator_new (GTK_ORIENTATION_HORIZONTAL);
+    gtk_widget_show (current);
+    gtk_list_box_row_set_header (row, current);
+  }
+}
+
+static void
+on_edge_reached (GtkScrolledWindow *scrolled,
+                 GtkPositionType    pos,
+                 gpointer           user_data)
+{
+  EphyHistoryDialog *self = EPHY_HISTORY_DIALOG (user_data);
+
+  if (pos == GTK_POS_BOTTOM) {
+    remove_pending_sorter_source (self, FALSE);
+
+    self->num_fetch += NUM_FETCH_LIMIT;
+    self->sorter_source = g_idle_add ((GSourceFunc)add_urls_source, self);
+  }
+}
+
 static void
 ephy_history_dialog_class_init (EphyHistoryDialogClass *klass)
 {
@@ -677,70 +738,16 @@ 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, liststore);
-  gtk_widget_class_bind_template_child (widget_class, EphyHistoryDialog, treeview);
-  gtk_widget_class_bind_template_child (widget_class, EphyHistoryDialog, tree_selection);
-  gtk_widget_class_bind_template_child (widget_class, EphyHistoryDialog, date_column);
-  gtk_widget_class_bind_template_child (widget_class, EphyHistoryDialog, name_column);
-  gtk_widget_class_bind_template_child (widget_class, EphyHistoryDialog, location_column);
-  gtk_widget_class_bind_template_child (widget_class, EphyHistoryDialog, date_renderer);
-  gtk_widget_class_bind_template_child (widget_class, EphyHistoryDialog, location_renderer);
-  gtk_widget_class_bind_template_child (widget_class, EphyHistoryDialog, treeview_popup_menu_model);
+  gtk_widget_class_bind_template_child (widget_class, EphyHistoryDialog, listbox);
   gtk_widget_class_bind_template_child (widget_class, EphyHistoryDialog, forget_all_button);
-  gtk_widget_class_bind_template_child (widget_class, EphyHistoryDialog, forget_button);
+  gtk_widget_class_bind_template_child (widget_class, EphyHistoryDialog, popover_menu);
 
-  gtk_widget_class_bind_template_callback (widget_class, on_treeview_row_activated);
-  gtk_widget_class_bind_template_callback (widget_class, on_treeview_key_press_event);
-  gtk_widget_class_bind_template_callback (widget_class, on_treeview_button_press_event);
-  gtk_widget_class_bind_template_callback (widget_class, on_treeview_selection_changed);
-  gtk_widget_class_bind_template_callback (widget_class, on_treeview_column_clicked_event);
+  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_search_entry_changed);
-  gtk_widget_class_bind_template_callback (widget_class, on_search_key_press_event);
-}
-
-static void
-convert_date_data_func (GtkTreeViewColumn *column,
-                        GtkCellRenderer   *renderer,
-                        GtkTreeModel      *model,
-                        GtkTreeIter       *iter,
-                        gpointer           user_data)
-{
-  int col_id = GPOINTER_TO_INT (user_data);
-  gint64 value;
-  char *friendly;
-
-  gtk_tree_model_get (model, iter,
-                      col_id,
-                      &value,
-                      -1);
-
-  /* Convert back to seconds. */
-  friendly = ephy_time_helpers_utf_friendly_time (value / 1000000);
-  g_object_set (renderer, "text", friendly, NULL);
-  g_free (friendly);
-}
-
-static void
-convert_location_data_func (GtkTreeViewColumn *column,
-                            GtkCellRenderer   *renderer,
-                            GtkTreeModel      *model,
-                            GtkTreeIter       *iter,
-                            gpointer           user_data)
-{
-  int col_id = GPOINTER_TO_INT (user_data);
-  char *url;
-  char *decoded_url;
-
-  gtk_tree_model_get (model, iter,
-                      col_id,
-                      &url,
-                      -1);
-  decoded_url = ephy_uri_decode (url);
-
-  g_object_set (renderer, "text", decoded_url, NULL);
-
-  g_free (url);
-  g_free (decoded_url);
+  gtk_widget_class_bind_template_callback (widget_class, on_edge_reached);
 }
 
 GtkWidget *
@@ -751,7 +758,6 @@ ephy_history_dialog_new (EphyHistoryService *history_service)
   g_assert (history_service != NULL);
 
   self = g_object_new (EPHY_TYPE_HISTORY_DIALOG,
-                       "use-header-bar", TRUE,
                        "history-service", history_service,
                        NULL);
 
@@ -788,45 +794,22 @@ ephy_history_dialog_init (EphyHistoryDialog *self)
   self->cancellable = g_cancellable_new ();
 
   self->urls = NULL;
-  self->sort_ascending = FALSE;
-  self->sort_column = COLUMN_DATE;
   self->sorter_source = 0;
 
+  gtk_list_box_set_header_func (GTK_LIST_BOX (self->listbox), box_header_func, NULL, NULL);
   ephy_gui_ensure_window_group (GTK_WINDOW (self));
 
-  g_object_set_data (G_OBJECT (self->date_column),
-                     "column", GINT_TO_POINTER (COLUMN_DATE));
-  g_object_set_data (G_OBJECT (self->name_column),
-                     "column", GINT_TO_POINTER (COLUMN_NAME));
-  g_object_set_data (G_OBJECT (self->location_column),
-                     "column", GINT_TO_POINTER (COLUMN_LOCATION));
-
-  gtk_tree_view_column_set_cell_data_func (GTK_TREE_VIEW_COLUMN (self->date_column),
-                                           GTK_CELL_RENDERER (self->date_renderer),
-                                           (GtkTreeCellDataFunc)convert_date_data_func,
-                                           GINT_TO_POINTER (COLUMN_DATE),
-                                           NULL);
-
-  gtk_tree_view_column_set_cell_data_func (GTK_TREE_VIEW_COLUMN (self->location_column),
-                                           GTK_CELL_RENDERER (self->location_renderer),
-                                           (GtkTreeCellDataFunc)convert_location_data_func,
-                                           GINT_TO_POINTER (COLUMN_LOCATION),
-                                           NULL);
-
   self->action_group = create_action_group (self);
   gtk_widget_insert_action_group (GTK_WIDGET (self), "history", self->action_group);
 
   if (ephy_embed_shell_get_mode (shell) == EPHY_EMBED_SHELL_MODE_INCOGNITO) {
     tooltip = _("It is not possible to modify history when in incognito mode.");
     gtk_widget_set_tooltip_text (self->forget_all_button, tooltip);
-    gtk_widget_set_tooltip_text (self->forget_button, tooltip);
-
-    action = g_action_map_lookup_action (G_ACTION_MAP (self->action_group), "forget");
-    g_simple_action_set_enabled (G_SIMPLE_ACTION (action), FALSE);
 
     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);
+    update_selection_actions (self->action_group, FALSE);
+  }
 }
+
diff --git a/src/ephy-history-dialog.h b/src/ephy-history-dialog.h
index d32ba7968..a32e57a9d 100644
--- a/src/ephy-history-dialog.h
+++ b/src/ephy-history-dialog.h
@@ -28,7 +28,7 @@ G_BEGIN_DECLS
 
 #define EPHY_TYPE_HISTORY_DIALOG (ephy_history_dialog_get_type ())
 
-G_DECLARE_FINAL_TYPE (EphyHistoryDialog, ephy_history_dialog, EPHY, HISTORY_DIALOG, GtkDialog)
+G_DECLARE_FINAL_TYPE (EphyHistoryDialog, ephy_history_dialog, EPHY, HISTORY_DIALOG, GtkWindow)
 
 GtkWidget      *ephy_history_dialog_new        (EphyHistoryService *history_service);
 
diff --git a/src/resources/gtk/history-dialog.ui b/src/resources/gtk/history-dialog.ui
index d86534df9..26f1cac25 100644
--- a/src/resources/gtk/history-dialog.ui
+++ b/src/resources/gtk/history-dialog.ui
@@ -1,53 +1,30 @@
 <?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.22.1 -->
 <interface>
-  <requires lib="gtk+" version="3.10"/>
-  <object class="GtkListStore" id="liststore">
-    <columns>
-      <!-- column-name DATE -->
-      <column type="gint64"/>
-      <!-- column-name TITLE -->
-      <column type="gchararray"/>
-      <!-- column-name LOCATION -->
-      <column type="gchararray"/>
-      <!-- column-name SYNC_ID -->
-      <column type="gchararray"/>
-    </columns>
-  </object>
-  <menu id="treeview_popup_menu_model">
-    <section>
-      <item>
-        <attribute name="label" translatable="yes">_Open</attribute>
-        <attribute name="action">history.open-selection</attribute>
-      </item>
-      <item>
-        <attribute name="label" translatable="yes">_Copy Location</attribute>
-        <attribute name="action">history.copy-url</attribute>
-      </item>
-      <item>
-        <attribute name="label" translatable="yes">_Delete</attribute>
-        <attribute name="action">history.forget</attribute>
-      </item>
-    </section>
-  </menu>
-  <template class="EphyHistoryDialog" parent="GtkDialog">
-    <property name="height_request">500</property>
+  <requires lib="gtk+" version="3.20"/>
+  <requires lib="libhandy" version="0.0"/>
+  <template class="EphyHistoryDialog" parent="GtkWindow">
+    <property name="can_focus">False</property>
     <property name="modal">True</property>
-    <property name="window_position">center</property>
+    <property name="window_position">center-on-parent</property>
     <property name="default_width">1000</property>
     <property name="default_height">600</property>
     <property name="destroy_with_parent">True</property>
     <property name="type_hint">dialog</property>
-    <child internal-child="headerbar">
+    <child type="titlebar">
       <object class="GtkHeaderBar">
+        <property name="visible">True</property>
+        <property name="can_focus">False</property>
         <property name="title" translatable="yes">History</property>
-        <property name="show-close-button">True</property>
+        <property name="show_close_button">True</property>
         <child>
           <object class="GtkButton" id="forget_all_button">
-            <property name="label" translatable="yes">C_lear All</property>
+            <property name="label" translatable="yes">C_lear all</property>
             <property name="visible">True</property>
+            <property name="can_focus">True</property>
+            <property name="receives_default">True</property>
+            <property name="action_name">history.forget-all</property>
             <property name="use_underline">True</property>
-            <property name="valign">center</property>
-            <property name="action-name">history.forget-all</property>
             <style>
               <class name="destructive-action"/>
               <class name="text-button"/>
@@ -56,177 +33,146 @@
         </child>
       </object>
     </child>
-    <child internal-child="vbox">
+    <child>
       <object class="GtkBox">
-        <property name="border_width">5</property>
-        <property name="spacing">6</property>
+        <property name="visible">True</property>
+        <property name="can_focus">False</property>
+        <property name="orientation">vertical</property>
         <child>
-          <object class="GtkSearchEntry">
+          <object class="GtkSearchBar">
             <property name="visible">True</property>
-            <property name="primary_icon_name">edit-find-symbolic</property>
-            <property name="primary_icon_activatable">False</property>
-            <property name="primary_icon_sensitive">False</property>
-            <property name="placeholder_text" translatable="yes">Search history</property>
-            <signal name="search-changed" handler="on_search_entry_changed"/>
-            <signal name="key-press-event" handler="on_search_key_press_event"/>
-          </object>
-        </child>
-        <child>
-          <object class="GtkBox">
-            <property name="visible">True</property>
-            <property name="orientation">vertical</property>
+            <property name="can_focus">False</property>
+            <property name="search_mode_enabled">True</property>
             <child>
-              <object class="GtkScrolledWindow">
-                <property name="width_request">400</property>
-                <property name="height_request">300</property>
+              <object class="GtkSearchEntry">
+                <property name="width_request">280</property>
                 <property name="visible">True</property>
-                <property name="expand">True</property>
-                <property name="shadow_type">in</property>
-                <property name="min_content_width">300</property>
-                <property name="min_content_height">300</property>
-                <child>
-                  <object class="GtkTreeView" id="treeview">
-                    <property name="visible">True</property>
-                    <property name="model">liststore</property>
-                    <property name="enable_search">False</property>
-                    <property name="search_column">0</property>
-                    <property name="fixed-height-mode">True</property>
-                    <signal name="button-press-event" handler="on_treeview_button_press_event"/>
-                    <signal name="key-press-event" handler="on_treeview_key_press_event"/>
-                    <signal name="row-activated" handler="on_treeview_row_activated"/>
-                    <child internal-child="selection">
-                      <object class="GtkTreeSelection" id="tree_selection">
-                        <property name="mode">multiple</property>
-                        <signal name="changed" handler="on_treeview_selection_changed"/>
-                      </object>
-                    </child>
-                    <child>
-                      <object class="GtkTreeViewColumn" id="date_column">
-                        <property name="sizing">fixed</property>
-                        <property name="fixed-width">150</property>
-                        <property name="title" translatable="yes">Date</property>
-                        <property name="clickable">True</property>
-                        <property name="sort_indicator">True</property>
-                        <property name="sort_order">descending</property>
-                        <signal name="clicked" handler="on_treeview_column_clicked_event"/>
-                        <child>
-                          <object class="GtkCellRendererText" id="date_renderer"/>
-                          <attributes>
-                            <attribute name="text">0</attribute>
-                          </attributes>
-                        </child>
-                      </object>
-                    </child>
-                    <child>
-                      <object class="GtkTreeViewColumn" id="name_column">
-                        <property name="sizing">fixed</property>
-                        <property name="fixed-width">500</property>
-                        <property name="title" translatable="yes">Name</property>
-                        <property name="clickable">True</property>
-                        <signal name="clicked" handler="on_treeview_column_clicked_event"/>
-                        <child>
-                          <object class="GtkCellRendererText">
-                            <property name="ellipsize">end</property>
-                          </object>
-                          <attributes>
-                            <attribute name="text">1</attribute>
-                          </attributes>
-                        </child>
-                      </object>
-                    </child>
-                    <child>
-                      <object class="GtkTreeViewColumn" id="location_column">
-                        <property name="sizing">fixed</property>
-                        <property name="fixed-width">200</property>
-                        <property name="title" translatable="yes">Location</property>
-                        <property name="clickable">True</property>
-                        <signal name="clicked" handler="on_treeview_column_clicked_event"/>
-                        <child>
-                          <object class="GtkCellRendererText" id="location_renderer">
-                            <property name="ellipsize">end</property>
-                          </object>
-                          <attributes>
-                            <attribute name="text">2</attribute>
-                          </attributes>
-                        </child>
-                      </object>
-                    </child>
-                  </object>
-                </child>
+                <property name="can_focus">True</property>
+                <property name="primary_icon_name">edit-find-symbolic</property>
+                <property name="primary_icon_activatable">False</property>
+                <property name="primary_icon_sensitive">False</property>
+                <property name="placeholder_text" translatable="yes">Search history</property>
+                <signal name="search-changed" handler="on_search_entry_changed" swapped="no"/>
               </object>
             </child>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">True</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkScrolledWindow" id="scrolled_window">
+            <property name="visible">True</property>
+            <property name="can_focus">True</property>
+            <signal name="edge-reached" handler="on_edge_reached" object="EphyHistoryDialog" swapped="no"/>
             <child>
-              <object class="GtkToolbar">
+              <object class="GtkViewport">
                 <property name="visible">True</property>
+                <property name="can_focus">False</property>
                 <child>
-                  <object class="GtkToolItem">
+                  <object class="HdyColumn">
                     <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="margin_start">6</property>
+                    <property name="margin_end">6</property>
+                    <property name="maximum_width">600</property>
+                    <property name="linear_growth_width">400</property>
                     <child>
-                      <object class="GtkBox">
+                      <object class="GtkFrame">
                         <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="margin_top">6</property>
+                        <property name="margin_bottom">6</property>
+                        <property name="label_xalign">0</property>
+                        <property name="shadow_type">in</property>
                         <child>
-                          <object class="GtkButton" id="forget_button">
+                          <object class="GtkListBox" id="listbox">
                             <property name="visible">True</property>
-                            <property name="tooltip_text" translatable="yes">Remove the selected pages from 
history</property>
-                            <property name="action-name">history.forget</property>
-                            <child>
-                              <object class="GtkImage">
-                                <property name="visible">True</property>
-                                <property name="icon_name">list-remove-symbolic</property>
-                              </object>
-                            </child>
+                            <property name="can_focus">False</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"/>
+                            <signal name="key-press-event" handler="on_listbox_key_press_event" 
swapped="no"/>
+                            <signal name="row-activated" handler="on_listbox_row_activated" swapped="no"/>
                           </object>
                         </child>
-                      </object>
-                    </child>
-                  </object>
-                  <packing>
-                    <property name="homogeneous">True</property>
-                  </packing>
-                </child>
-                <child>
-                  <object class="GtkSeparatorToolItem">
-                    <property name="visible">True</property>
-                    <property name="draw">False</property>
-                  </object>
-                  <packing>
-                    <property name="expand">True</property>
-                  </packing>
-                </child>
-                <child>
-                  <object class="GtkToolItem">
-                    <property name="visible">True</property>
-                    <child>
-                      <object class="GtkBox">
-                        <property name="visible">True</property>
-                        <child>
-                          <object class="GtkButton">
-                            <property name="visible">True</property>
-                            <property name="tooltip_text" translatable="yes">Open the selected pages in new 
tabs</property>
-                            <property name="action-name">history.open-selection</property>
-                            <child>
-                              <object class="GtkImage">
-                                <property name="visible">True</property>
-                                <property name="icon_name">go-jump-symbolic</property>
-                              </object>
-                            </child>
-                          </object>
+                        <child type="label_item">
+                          <placeholder/>
                         </child>
                       </object>
                     </child>
                   </object>
-                  <packing>
-                    <property name="homogeneous">True</property>
-                  </packing>
                 </child>
-                <style>
-                  <class name="inline-toolbar"/>
-                </style>
               </object>
             </child>
           </object>
+          <packing>
+            <property name="expand">True</property>
+            <property name="fill">True</property>
+            <property name="position">1</property>
+          </packing>
         </child>
       </object>
     </child>
   </template>
+  <object class="GtkPopoverMenu" id="popover_menu">
+    <property name="can_focus">False</property>
+    <property name="border_width">6</property>
+    <child>
+      <object class="GtkBox">
+        <property name="visible">True</property>
+        <property name="can_focus">False</property>
+        <property name="orientation">vertical</property>
+        <property name="spacing">6</property>
+        <child>
+          <object class="GtkModelButton">
+            <property name="visible">True</property>
+            <property name="can_focus">True</property>
+            <property name="receives_default">True</property>
+            <property name="action_name">history.open-selection</property>
+            <property name="text" translatable="yes">_Open</property>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">True</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkModelButton">
+            <property name="visible">True</property>
+            <property name="can_focus">True</property>
+            <property name="receives_default">True</property>
+            <property name="action_name">history.copy-url</property>
+            <property name="text" translatable="yes">_Copy Location</property>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">True</property>
+            <property name="position">1</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkModelButton">
+            <property name="visible">True</property>
+            <property name="can_focus">True</property>
+            <property name="receives_default">True</property>
+            <property name="action_name">history.forget</property>
+            <property name="text" translatable="yes">_Delete</property>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">True</property>
+            <property name="position">2</property>
+          </packing>
+        </child>
+      </object>
+      <packing>
+        <property name="submenu">main</property>
+        <property name="position">1</property>
+      </packing>
+    </child>
+  </object>
 </interface>



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