gnome: add contact/history sorting

Refs #73115

Change-Id: I7fee71c7ef330c705a010ed1aa75ff1ec1b6d426
diff --git a/src/contactsview.cpp b/src/contactsview.cpp
index 4414a8a..8b0f93e 100644
--- a/src/contactsview.cpp
+++ b/src/contactsview.cpp
@@ -32,7 +32,6 @@
 
 #include <gtk/gtk.h>
 #include "models/gtkqsortfiltertreemodel.h"
-#include "models/activeitemproxymodel.h"
 #include <categorizedcontactmodel.h>
 #include <personmodel.h>
 #include "utils/calling.h"
@@ -41,6 +40,7 @@
 #include <contactmethod.h>
 #include "defines.h"
 #include "utils/models.h"
+#include <QtCore/QItemSelectionModel>
 
 #define COPY_DATA_KEY "copy_data"
 
@@ -58,7 +58,7 @@
 
 struct _ContactsViewPrivate
 {
-    ActiveItemProxyModel *q_contact_model;
+    CategorizedContactModel::SortedProxy *q_sorted_proxy;
 };
 
 G_DEFINE_TYPE_WITH_PRIVATE(ContactsView, contacts_view, GTK_TYPE_BOX);
@@ -351,15 +351,20 @@
      * otherwise the search steals input focus on key presses */
     gtk_tree_view_set_enable_search(GTK_TREE_VIEW(treeview_contacts), FALSE);
 
+    /* initial set up to be categorized by name and sorted alphabetically */
+    priv->q_sorted_proxy = CategorizedContactModel::SortedProxy::instance();
     CategorizedContactModel::instance()->setUnreachableHidden(true);
-    priv->q_contact_model = new ActiveItemProxyModel(CategorizedContactModel::instance());
-    priv->q_contact_model->setSortRole(Qt::DisplayRole);
-    priv->q_contact_model->setSortLocaleAware(true);
-    priv->q_contact_model->setSortCaseSensitivity(Qt::CaseInsensitive);
-    priv->q_contact_model->sort(0);
+
+    /* for now we always want to sort by ascending order */
+    priv->q_sorted_proxy->model()->sort(0);
+
+    /* select default category (the first one, which is by name) */
+    priv->q_sorted_proxy->categorySelectionModel()->setCurrentIndex(
+        priv->q_sorted_proxy->categoryModel()->index(0, 0),
+        QItemSelectionModel::ClearAndSelect);
 
     GtkQSortFilterTreeModel *contact_model = gtk_q_sort_filter_tree_model_new(
-        (QSortFilterProxyModel *)priv->q_contact_model,
+        priv->q_sorted_proxy->model(),
         1,
         Qt::DisplayRole, G_TYPE_STRING);
     gtk_tree_view_set_model(GTK_TREE_VIEW(treeview_contacts), GTK_TREE_MODEL(contact_model));
@@ -413,11 +418,6 @@
 static void
 contacts_view_finalize(GObject *object)
 {
-    ContactsView *self = CONTACTS_VIEW(object);
-    ContactsViewPrivate *priv = CONTACTS_VIEW_GET_PRIVATE(self);
-
-    delete priv->q_contact_model;
-
     G_OBJECT_CLASS(contacts_view_parent_class)->finalize(object);
 }
 
@@ -434,4 +434,4 @@
     gpointer self = g_object_new(CONTACTS_VIEW_TYPE, NULL);
 
     return (GtkWidget *)self;
-}
\ No newline at end of file
+}
diff --git a/src/contactsview.h b/src/contactsview.h
index df0c0ae..43c097d 100644
--- a/src/contactsview.h
+++ b/src/contactsview.h
@@ -44,8 +44,9 @@
 typedef struct _ContactsView      ContactsView;
 typedef struct _ContactsViewClass ContactsViewClass;
 
-GType      contacts_view_get_type (void) G_GNUC_CONST;
-GtkWidget *contacts_view_new      (void);
+GType      contacts_view_get_type  (void) G_GNUC_CONST;
+GtkWidget *contacts_view_new       (void);
+// void       contact_view_set_sorting(ContactsView *self, int sort);
 
 G_END_DECLS
 
diff --git a/src/historyview.cpp b/src/historyview.cpp
index f28b762..4ee3e43 100644
--- a/src/historyview.cpp
+++ b/src/historyview.cpp
@@ -42,6 +42,7 @@
 #include "utils/models.h"
 #include <contactmethod.h>
 #include <QtCore/QDateTime> // for date time formatting
+#include <QtCore/QItemSelectionModel>
 
 struct _HistoryView
 {
@@ -57,7 +58,8 @@
 
 struct _HistoryViewPrivate
 {
-    QSortFilterProxyModel *q_history_model;
+    CategorizedHistoryModel::SortedProxy *q_sorted_proxy;
+    QMetaObject::Connection category_changed;
 };
 
 G_DEFINE_TYPE_WITH_PRIVATE(HistoryView, history_view, GTK_TYPE_BOX);
@@ -195,16 +197,6 @@
 }
 
 static void
-expand_if_child(G_GNUC_UNUSED GtkTreeModel *tree_model,
-                GtkTreePath  *path,
-                G_GNUC_UNUSED GtkTreeIter  *iter,
-                GtkTreeView  *treeview)
-{
-    if (gtk_tree_path_get_depth(path) == 2)
-        gtk_tree_view_expand_to_path(treeview, path);
-}
-
-static void
 render_call_photo(G_GNUC_UNUSED GtkTreeViewColumn *tree_column,
                      GtkCellRenderer *cell,
                      GtkTreeModel *tree_model,
@@ -353,14 +345,35 @@
      * otherwise the search steals input focus on key presses */
     gtk_tree_view_set_enable_search(GTK_TREE_VIEW(treeview_history), FALSE);
 
-    /* sort the history in descending order by date */
-    priv->q_history_model = new QSortFilterProxyModel();
-    priv->q_history_model->setSourceModel(CategorizedHistoryModel::instance());
-    priv->q_history_model->setSortRole(static_cast<int>(Call::Role::Date));
-    priv->q_history_model->sort(0,Qt::DescendingOrder);
+    /* instantiate history proxy model */
+    priv->q_sorted_proxy = CategorizedHistoryModel::SortedProxy::instance();
+
+    /* for now there is no way in the UI to pick whether sorting is ascending
+     * or descending, so we do it in the code when the category changes */
+    priv->category_changed = QObject::connect(
+        priv->q_sorted_proxy->categorySelectionModel(),
+        &QItemSelectionModel::currentChanged,
+        [=] (const QModelIndex &current, G_GNUC_UNUSED const QModelIndex &previous)
+        {
+            if (current.isValid()) {
+                if (current.row() == 0) {
+                    /* sort in descending order for the date */
+                    priv->q_sorted_proxy->model()->sort(0, Qt::DescendingOrder);
+                } else {
+                    /* ascending order for verything else */
+                    priv->q_sorted_proxy->model()->sort(0);
+                }
+            }
+        }
+    );
+
+    /* select default category (the first one, which is by date) */
+    priv->q_sorted_proxy->categorySelectionModel()->setCurrentIndex(
+        priv->q_sorted_proxy->categoryModel()->index(0, 0),
+        QItemSelectionModel::ClearAndSelect);
 
     GtkQSortFilterTreeModel *history_model = gtk_q_sort_filter_tree_model_new(
-        priv->q_history_model,
+        priv->q_sorted_proxy->model(),
         5,
         Qt::DisplayRole, G_TYPE_STRING,
         Call::Role::Number, G_TYPE_STRING,
@@ -444,14 +457,8 @@
     gtk_tree_view_append_column(GTK_TREE_VIEW(treeview_history), column);
     gtk_tree_view_column_set_resizable(column, TRUE);
 
-    /* expand the first row, which should be the most recent calls */
-    gtk_tree_view_expand_row(GTK_TREE_VIEW(treeview_history),
-                             gtk_tree_path_new_from_string("0"),
-                             FALSE);
-
     g_signal_connect(treeview_history, "row-activated", G_CALLBACK(activate_history_item), NULL);
     g_signal_connect(treeview_history, "button-press-event", G_CALLBACK(history_popup_menu), treeview_history);
-    g_signal_connect(history_model, "row-inserted", G_CALLBACK(expand_if_child), treeview_history);
 
     gtk_widget_show_all(GTK_WIDGET(self));
 }
@@ -459,17 +466,16 @@
 static void
 history_view_dispose(GObject *object)
 {
+    HistoryViewPrivate *priv = HISTORY_VIEW_GET_PRIVATE(object);
+
+    QObject::disconnect(priv->category_changed);
+
     G_OBJECT_CLASS(history_view_parent_class)->dispose(object);
 }
 
 static void
 history_view_finalize(GObject *object)
 {
-    HistoryView *self = HISTORY_VIEW(object);
-    HistoryViewPrivate *priv = HISTORY_VIEW_GET_PRIVATE(self);
-
-    delete priv->q_history_model;
-
     G_OBJECT_CLASS(history_view_parent_class)->finalize(object);
 }
 
@@ -486,4 +492,4 @@
     gpointer self = g_object_new(HISTORY_VIEW_TYPE, NULL);
 
     return (GtkWidget *)self;
-}
\ No newline at end of file
+}
diff --git a/src/ringmainwindow.cpp b/src/ringmainwindow.cpp
index d1acd36..7d6b314 100644
--- a/src/ringmainwindow.cpp
+++ b/src/ringmainwindow.cpp
@@ -56,6 +56,7 @@
 #include "utils/calling.h"
 #include "frequentcontactsview.h"
 #include "contactsview.h"
+#include <categorizedcontactmodel.h>
 #include "historyview.h"
 #include "utils/models.h"
 #include "generalsettingsview.h"
@@ -97,6 +98,8 @@
     GtkWidget *radiobutton_contacts;
     GtkWidget *radiobutton_history;
     GtkWidget *radiobutton_presence;
+    GtkWidget *combobox_history_sort;
+    GtkWidget *combobox_contacts_sort;
     GtkWidget *vbox_left_pane;
     GtkWidget *vbox_contacts;
     GtkWidget *search_entry;
@@ -327,6 +330,10 @@
                        (GSourceFunc)grab_focus_on_widget,
                        gtk_stack_get_visible_child(GTK_STACK(priv->stack_contacts_history_presence)),
                        NULL);
+
+    /* show the correct sorting combobox */
+    gtk_widget_show(priv->combobox_contacts_sort);
+    gtk_widget_hide(priv->combobox_history_sort);
 }
 
 static void
@@ -346,6 +353,10 @@
                        (GSourceFunc)grab_focus_on_widget,
                        gtk_stack_get_visible_child(GTK_STACK(priv->stack_contacts_history_presence)),
                        NULL);
+
+    /* show the correct sorting combobox */
+    gtk_widget_hide(priv->combobox_contacts_sort);
+    gtk_widget_hide(priv->combobox_history_sort);
 }
 
 static void
@@ -365,6 +376,10 @@
                        (GSourceFunc)grab_focus_on_widget,
                        gtk_stack_get_visible_child(GTK_STACK(priv->stack_contacts_history_presence)),
                        NULL);
+
+    /* show the correct sorting combobox */
+    gtk_widget_hide(priv->combobox_contacts_sort);
+    gtk_widget_show(priv->combobox_history_sort);
 }
 
 static gboolean
@@ -994,12 +1009,18 @@
     gtk_stack_add_named(GTK_STACK(priv->stack_contacts_history_presence),
                         contacts_view,
                         VIEW_CONTACTS);
+    gtk_combo_box_set_qmodel(GTK_COMBO_BOX(priv->combobox_contacts_sort),
+                             (QAbstractItemModel *)CategorizedContactModel::SortedProxy::instance()->categoryModel(),
+                             CategorizedContactModel::SortedProxy::instance()->categorySelectionModel());
 
     /* history view */
     GtkWidget *history_view = history_view_new();
     gtk_stack_add_named(GTK_STACK(priv->stack_contacts_history_presence),
                         history_view,
                         VIEW_HISTORY);
+    gtk_combo_box_set_qmodel(GTK_COMBO_BOX(priv->combobox_history_sort),
+                             (QAbstractItemModel *)CategorizedHistoryModel::SortedProxy::instance()->categoryModel(),
+                             CategorizedHistoryModel::SortedProxy::instance()->categorySelectionModel());
 
     /* presence view/model */
     GtkWidget *scrolled_window = gtk_scrolled_window_new(NULL, NULL);
@@ -1018,6 +1039,8 @@
 
     /* TODO: make this linked to the client settings so that the last shown view is the same on startup */
     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(priv->radiobutton_contacts), TRUE);
+    gtk_widget_show(priv->combobox_contacts_sort);
+    gtk_widget_hide(priv->combobox_history_sort);
 
     /* TODO: replace stack paceholder view */
     GtkWidget *placeholder_view = gtk_tree_view_new();
@@ -1177,6 +1200,8 @@
     gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, radiobutton_contacts);
     gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, radiobutton_history);
     gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, radiobutton_presence);
+    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, combobox_history_sort);
+    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, combobox_contacts_sort);
     gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, ring_menu);
     gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, image_ring);
     gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, ring_settings);
diff --git a/src/utils/models.cpp b/src/utils/models.cpp
index 59da48e..f6d6d31 100644
--- a/src/utils/models.cpp
+++ b/src/utils/models.cpp
@@ -181,11 +181,14 @@
     gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(box), renderer,
                                    "text", 0, NULL);
 
+   /* sync the initial selection */
+   gtk_combo_box_set_active_index(box, selection_model->currentIndex());
+
     /* connect signals to and from the selection model */
     connection = QObject::connect(
         selection_model,
         &QItemSelectionModel::currentChanged,
-        [=](const QModelIndex & current, G_GNUC_UNUSED const QModelIndex & previous) {
+        [=](const QModelIndex current, G_GNUC_UNUSED const QModelIndex & previous) {
             gtk_combo_box_set_active_index(box, current);
         }
     );
@@ -194,8 +197,5 @@
                      G_CALLBACK(update_selection),
                      selection_model);
 
-    /* sync the initial selection */
-    gtk_combo_box_set_active_index(box, selection_model->currentIndex());
-
     return connection;
-}
\ No newline at end of file
+}
diff --git a/ui/ringmainwindow.ui b/ui/ringmainwindow.ui
index 3d07d83..cdc2d4a 100644
--- a/ui/ringmainwindow.ui
+++ b/ui/ringmainwindow.ui
@@ -211,15 +211,14 @@
                 <property name="visible">True</property>
                 <property name="can_focus">False</property>
                 <property name="halign">center</property>
-                <property name="homogeneous">True</property>
                 <property name="margin-top">5</property>
                 <style>
                   <class name="linked"/>
                 </style>
                 <child>
                   <object class="GtkRadioButton" id="radiobutton_contacts">
-                    <property name="label" translatable="yes">contacts</property>
-                    <!-- TODO: make visible when feature ready -->
+                    <property name="label" translatable="yes">Contacts</property>
+                    <property name="tooltip-text">Contacts</property>
                     <property name="visible">True</property>
                     <property name="image">image_contacts</property>
                     <property name="draw_indicator">False</property>
@@ -228,7 +227,8 @@
                 </child>
                 <child>
                   <object class="GtkRadioButton" id="radiobutton_history">
-                    <property name="label" translatable="yes">history</property>
+                    <property name="label" translatable="yes">History</property>
+                    <property name="tooltip-text">History</property>
                     <property name="visible">True</property>
                     <property name="image">image_history</property>
                     <property name="draw_indicator">False</property>
@@ -238,7 +238,8 @@
                 </child>
                 <child>
                   <object class="GtkRadioButton" id="radiobutton_presence">
-                    <property name="label" translatable="yes">online</property>
+<!--                     <property name="label" translatable="yes">online</property> -->
+                    <property name="tooltip-text">Presence</property>
                      <!-- TODO: make visible when feature ready -->
                     <property name="visible">False</property>
                     <property name="image">image_presence</property>
@@ -247,6 +248,26 @@
                   </object>
                   <packing></packing>
                 </child>
+                <child>
+                  <object class="GtkComboBox" id="combobox_history_sort">
+                    <!-- set to visible when showing history view -->
+                    <property name="visible">False</property>
+                    <property name="can_focus">True</property>
+                    <!-- TODO: save the selected sorting in settings -->
+                    <property name="active">0</property>
+                  </object>
+                  <packing></packing>
+                </child>
+                <child>
+                  <object class="GtkComboBox" id="combobox_contacts_sort">
+                    <!-- set to visible when showing contacts view -->
+                    <property name="visible">False</property>
+                    <property name="can_focus">True</property>
+                    <!-- TODO: save the selected sorting in settings -->
+                    <property name="active">0</property>
+                  </object>
+                  <packing></packing>
+                </child>
               </object>
               <packing>
                 <property name="expand">False</property>