From 3ef9ea4760232e56e04a75532c435ffa868d725e Mon Sep 17 00:00:00 2001 From: Alexandre Erwin Ittner Date: Sun, 5 Feb 2023 18:10:27 -0300 Subject: [PATCH 1/3] Add support for enum keys in configuration --- src/conf.c | 20 ++++++++++++++++++++ src/conf.h | 21 +++++++++++++++++++++ 2 files changed, 41 insertions(+) diff --git a/src/conf.c b/src/conf.c index b50da57af..b46a71f2b 100644 --- a/src/conf.c +++ b/src/conf.c @@ -215,6 +215,13 @@ conf_set_int_value (const gchar *key, gint value) g_settings_set_int (settings, key, value); } +void +conf_set_enum_value (const gchar *key, gint value) +{ + g_assert (key != NULL); + g_settings_set_enum (settings, key, value); +} + gchar * conf_get_toolbar_style(void) { @@ -295,6 +302,19 @@ conf_get_int_value_from_schema (GSettings *gsettings, const gchar *key, gint *va return (NULL != value); } +gboolean +conf_get_enum_value_from_schema (GSettings *gsettings, const gchar *key, gint *value) +{ + g_assert (key != NULL); + g_assert (value != NULL); + + if (gsettings == NULL) + gsettings = settings; + *value = g_settings_get_enum (gsettings,key); + return (NULL != value); +} + + gboolean conf_get_default_font (gchar **value) { diff --git a/src/conf.h b/src/conf.h index 90d1e29b2..0766efa6b 100644 --- a/src/conf.h +++ b/src/conf.h @@ -95,6 +95,7 @@ void conf_deinit (void); #define conf_get_str_value(key, value) conf_get_str_value_from_schema (NULL, key, value) #define conf_get_strv_value(key, value) conf_get_strv_value_from_schema (NULL, key, value) #define conf_get_int_value(key, value) conf_get_int_value_from_schema (NULL, key, value) +#define conf_get_enum_value(key, value) conf_get_enum_value_from_schema (NULL, key, value) /** @@ -153,6 +154,17 @@ gboolean conf_get_strv_value_from_schema (GSettings *gsettings,const gchar *key, */ gboolean conf_get_int_value_from_schema (GSettings *gsettings, const gchar *key, gint *value); +/** + * Retrieves the value of the given enum configuration key. + * + * @param gsettings gsettings schema to use + * @param key the configuration key + * @param value the value, if the function returned FALSE it's always 0 + * + * @returns TRUE if the configuration key was found + */ +gboolean conf_get_enum_value_from_schema (GSettings *gsettings, const gchar *key, gint *value); + /** * Sets the value of the given boolean configuration key. * @@ -187,6 +199,15 @@ void conf_set_strv_value (const gchar *key, const gchar **value); */ void conf_set_int_value (const gchar *key, gint value); +/** + * Sets the value of the given enum configuration key + * + * @param key the configuration key + * @param value the new enum (as integer) value + */ +void conf_set_enum_value (const gchar *key, gint value); + + /** * Returns the current toolbar configuration. * From fe5d1e6fb821f8328eb93a1fed8170c2f91c4086 Mon Sep 17 00:00:00 2001 From: Alexandre Erwin Ittner Date: Sun, 5 Feb 2023 18:28:31 -0300 Subject: [PATCH 2/3] Add a "flat view" mode for the feed list Idea is to show all feeds in a single list, without folders and other grouping elements, acting as a alternate for the reduced view mode. Persisting the choice for this mode also requires adding new config keys to the gsettings schema. This is the first part of a two-commit change (actually split after a 'merge --squash') that will also add a filter box for feed titles. --- glade/liferea_menu.ui | 15 ++++- net.sf.liferea.gschema.xml.in | 10 ++++ src/conf.h | 2 +- src/ui/feed_list_view.c | 108 +++++++++++++++++++++++++--------- src/ui/feed_list_view.h | 28 ++++++++- src/ui/liferea_shell.c | 9 ++- 6 files changed, 136 insertions(+), 36 deletions(-) diff --git a/glade/liferea_menu.ui b/glade/liferea_menu.ui index b8a4be463..00d95d792 100644 --- a/glade/liferea_menu.ui +++ b/glade/liferea_menu.ui @@ -153,8 +153,19 @@
- app.reduced-feed-list - _Reduced Feed List + app.feedlist-view-mode + N_ormal feed list + normal + + + app.feedlist-view-mode + _Reduced feed list + reduced + + + app.feedlist-view-mode + F_lat feed list + flat
diff --git a/net.sf.liferea.gschema.xml.in b/net.sf.liferea.gschema.xml.in index 62ab62674..27fedf644 100644 --- a/net.sf.liferea.gschema.xml.in +++ b/net.sf.liferea.gschema.xml.in @@ -1,5 +1,10 @@ + + + + + @@ -132,6 +137,11 @@ Filter feeds without unread items from feed list. If this option is enabled the feed list will contain only feeds that have unread items. + + 'normal' + How the feed list is presented + How the feed list is presented to the user. Possible values are "normal", "reduced", and "flat". + '' Custom download command. diff --git a/src/conf.h b/src/conf.h index 0766efa6b..124089879 100644 --- a/src/conf.h +++ b/src/conf.h @@ -56,7 +56,7 @@ /* folder handling settings */ #define FOLDER_DISPLAY_MODE "folder-display-mode" #define FOLDER_DISPLAY_HIDE_READ "folder-display-hide-read" -#define REDUCED_FEEDLIST "reduced-feedlist" +#define FEEDLIST_VIEW_MODE "feedlist-view-mode" /* GUI settings and persistency values */ #define CONFIRM_MARK_ALL_READ "confirm-mark-all-read" diff --git a/src/ui/feed_list_view.c b/src/ui/feed_list_view.c index 60f910825..eec7966e4 100644 --- a/src/ui/feed_list_view.c +++ b/src/ui/feed_list_view.c @@ -51,7 +51,7 @@ struct _FeedListView { GHashTable *flIterHash; /**< hash table used for fast node id <-> tree iter lookup */ - gboolean feedlist_reduced_unread; /**< TRUE when feed list is in reduced mode (no folders, only unread feeds) */ + enum feedlistViewMode view_mode; }; enum { @@ -95,6 +95,42 @@ feed_list_view_init (FeedListView *f) { } + +enum feedlistViewMode +feed_list_view_mode_string_to_value (const gchar *str_mode) +{ + /* TODO: Load these values from Gsettings Schema. */ + if (!g_strcmp0 ("normal", str_mode)) { + return FEEDLIST_VIEW_MODE_NORMAL; + } + if (!g_strcmp0 ("reduced", str_mode)) { + return FEEDLIST_VIEW_MODE_REDUCED; + } + if (!g_strcmp0 ("flat", str_mode)) { + return FEEDLIST_VIEW_MODE_FLAT; + } + + /* Unknown or invalid mode, assume normal. */ + return FEEDLIST_VIEW_MODE_NORMAL; +} + +const gchar * +feed_list_view_mode_value_to_string (enum feedlistViewMode mode) +{ + /* TODO: Load these values from Gsettings Schema. */ + switch (mode) { + case FEEDLIST_VIEW_MODE_NORMAL: + return "normal"; + case FEEDLIST_VIEW_MODE_REDUCED: + return "reduced"; + case FEEDLIST_VIEW_MODE_FLAT: + return "flat"; + } + /* Unknown or invalid mode, assume normal. */ + return "normal"; +} + + static void feed_list_view_row_changed_cb (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter) { @@ -185,18 +221,24 @@ feed_list_view_filter_visible_function (GtkTreeModel *model, GtkTreeIter *iter, gint count; nodePtr node; - if (!flv->feedlist_reduced_unread) + if (flv->view_mode == FEEDLIST_VIEW_MODE_NORMAL) { return TRUE; + } + + /* From here, assume mode is REDUCED or FLAT */ gtk_tree_model_get (model, iter, FS_PTR, &node, FS_UNREAD, &count, -1); - if (!node) + if (!node) { return FALSE; + } - if (IS_NEWSBIN(node) && node->data && ((feedPtr)node->data)->alwaysShowInReduced) + if (IS_NEWSBIN(node) && node->data && ((feedPtr)node->data)->alwaysShowInReduced) { return TRUE; + } - if (IS_FOLDER (node) || IS_NODE_SOURCE (node)) + if (IS_FOLDER (node) || IS_NODE_SOURCE (node)) { return FALSE; + } if (IS_VFOLDER (node)) return TRUE; @@ -207,10 +249,11 @@ feed_list_view_filter_visible_function (GtkTreeModel *model, GtkTreeIter *iter, if (feedlist_get_selected () == node) return TRUE; - if (count > 0) - return TRUE; + if (flv->view_mode == FEEDLIST_VIEW_MODE_REDUCED && count == 0) { + return FALSE; + } - return FALSE; + return TRUE; } static void @@ -232,9 +275,9 @@ feed_list_view_restore_folder_expansion (nodePtr node) } static void -feed_list_view_reduce_mode_changed (void) +feed_list_view_mode_changed (void) { - if (flv->feedlist_reduced_unread) { + if (flv->view_mode != FEEDLIST_VIEW_MODE_NORMAL) { gtk_tree_view_set_reorderable (flv->treeview, FALSE); gtk_tree_view_set_model (flv->treeview, GTK_TREE_MODEL (flv->filter)); gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (flv->filter)); @@ -248,11 +291,11 @@ feed_list_view_reduce_mode_changed (void) } static void -feed_list_view_set_reduce_mode (gboolean newReduceMode) +feed_list_view_set_view_mode (enum feedlistViewMode mode) { - flv->feedlist_reduced_unread = newReduceMode; - conf_set_bool_value (REDUCED_FEEDLIST, flv->feedlist_reduced_unread); - feed_list_view_reduce_mode_changed (); + flv->view_mode = mode; + conf_set_enum_value (FEEDLIST_VIEW_MODE, flv->view_mode); + feed_list_view_mode_changed (); feed_list_view_reload_feedlist (); } @@ -285,7 +328,7 @@ feed_list_view_sort_folder (nodePtr folder) feed_list_view_reload_feedlist (); /* Reduce mode didn't actually change but we need to set the * correct model according to the setting in the same way : */ - feed_list_view_reduce_mode_changed (); + feed_list_view_mode_changed (); feedlist_foreach (feed_list_view_restore_folder_expansion); feedlist_schedule_save (); } @@ -359,9 +402,9 @@ feed_list_view_create (GtkTreeView *treeview) G_CALLBACK (feed_list_view_selection_changed_cb), flv); - conf_get_bool_value (REDUCED_FEEDLIST, &flv->feedlist_reduced_unread); - if (flv->feedlist_reduced_unread) - feed_list_view_reduce_mode_changed (); /* before menu setup for reduced mode check box to be correct */ + conf_get_enum_value (FEEDLIST_VIEW_MODE, (gint *) &flv->view_mode); + if (flv->view_mode != FEEDLIST_VIEW_MODE_NORMAL) + feed_list_view_mode_changed (); /* before menu setup for reduced mode check box to be correct */ ui_dnd_setup_feedlist (flv->feedstore); @@ -378,7 +421,7 @@ feed_list_view_select (nodePtr node) GtkTreePath *path = NULL; /* in filtered mode we need to convert the iterator */ - if (flv->feedlist_reduced_unread) { + if (flv->view_mode != FEEDLIST_VIEW_MODE_NORMAL) { GtkTreeIter iter; gboolean valid = gtk_tree_model_filter_convert_child_iter_to_iter (GTK_TREE_MODEL_FILTER (flv->filter), &iter, feed_list_view_to_iter (node->id)); if (valid) @@ -519,13 +562,20 @@ on_new_vfolder_activate (GSimpleAction *menuitem, GVariant *parameter, gpointer } void -on_feedlist_reduced_activate (GSimpleAction *action, GVariant *parameter, gpointer user_data) +on_feedlist_view_mode_activate (GSimpleAction *action, GVariant *parameter, gpointer user_data) { - GVariant *state = g_action_get_state (G_ACTION (action)); - gboolean val = !g_variant_get_boolean (state); - feed_list_view_set_reduce_mode (val); - g_simple_action_set_state (action, g_variant_new_boolean (val)); - g_object_unref (state); + const gchar *str_new_mode = g_variant_get_string (parameter, NULL); + enum feedlistViewMode new_mode = feed_list_view_mode_string_to_value (str_new_mode); + + GVariant *tmp = g_action_get_state (G_ACTION(action)); + const gchar *str_cur_mode = g_variant_get_string (tmp, NULL); + enum feedlistViewMode cur_mode = feed_list_view_mode_string_to_value (str_cur_mode); + g_variant_unref (tmp); + + if (new_mode != cur_mode) { + feed_list_view_set_view_mode (new_mode); + g_simple_action_set_state (action, g_variant_new_string (str_new_mode)); + } } // Handling feed list nodes @@ -569,7 +619,7 @@ feed_list_view_is_expanded (const gchar *nodeId) GtkTreeIter *iter; gboolean expanded = FALSE; - if (flv->feedlist_reduced_unread) + if (flv->view_mode != FEEDLIST_VIEW_MODE_NORMAL) return FALSE; iter = feed_list_view_to_iter (nodeId); @@ -588,7 +638,7 @@ feed_list_view_set_expansion (nodePtr folder, gboolean expanded) GtkTreeIter *iter; GtkTreePath *path; - if (flv->feedlist_reduced_unread) + if (flv->view_mode != FEEDLIST_VIEW_MODE_NORMAL) return; iter = feed_list_view_to_iter (folder->id); @@ -691,14 +741,14 @@ feed_list_view_add_node (nodePtr node) iter = g_new0 (GtkTreeIter, 1); /* if reduced feedlist, show flat treeview */ - if (flv->feedlist_reduced_unread) + if (flv->view_mode != FEEDLIST_VIEW_MODE_NORMAL) parentIter = NULL; else if (node->parent != feedlist_get_root ()) parentIter = feed_list_view_to_iter (node->parent->id); position = g_slist_index (node->parent->children, node); - if (flv->feedlist_reduced_unread || position < 0) + if ((flv->view_mode != FEEDLIST_VIEW_MODE_NORMAL) || position < 0) gtk_tree_store_append (flv->feedstore, iter, parentIter); else gtk_tree_store_insert (flv->feedstore, iter, parentIter, position); diff --git a/src/ui/feed_list_view.h b/src/ui/feed_list_view.h index 0446c0593..bd2fe2b21 100644 --- a/src/ui/feed_list_view.h +++ b/src/ui/feed_list_view.h @@ -40,6 +40,14 @@ enum { FS_LEN }; + +enum feedlistViewMode { + FEEDLIST_VIEW_MODE_NORMAL = 0, + FEEDLIST_VIEW_MODE_REDUCED = 1, + FEEDLIST_VIEW_MODE_FLAT = 2, +}; + + /** * feed_list_view_select: * @@ -84,7 +92,7 @@ void on_new_plugin_activate (GSimpleAction *menuitem, GVariant *parameter, gpoin void on_new_newsbin_activate (GSimpleAction *menuitem, GVariant *parameter, gpointer user_data); void on_new_vfolder_activate (GSimpleAction *menuitem, GVariant *parameter, gpointer user_data); -void on_feedlist_reduced_activate (GSimpleAction *action, GVariant *parameter, gpointer user_data); +void on_feedlist_view_mode_activate (GSimpleAction *action, GVariant *parameter, gpointer user_data); /** * Determines the tree iter of a given node. @@ -184,4 +192,22 @@ void feed_list_view_remove (nodePtr node); */ void feed_list_view_add_duplicate_url_subscription (subscriptionPtr tempSubscription, nodePtr exNode); +/** + * Return the integer value associated to the string representing the view mode. + * If the view mode is invalid or unrecognized, returns FEEDLIST_VIEW_MODE_NORMAL. + * + * @param str_mode A string representing a view mode. + * @return A integer relative to this mode. + */ +enum feedlistViewMode feed_list_view_mode_string_to_value (const gchar *str_mode); + +/** + * Return a read-only string representing the given view mode. + * The returned string must not be changed in any way. + * + * @param mode a FEEDLIST_VIEW_MODE_* integer. + * @return an internally allocated const string. + */ +const gchar * feed_list_view_mode_value_to_string (enum feedlistViewMode mode); + #endif diff --git a/src/ui/liferea_shell.c b/src/ui/liferea_shell.c index 492d483f6..5b3fd87e8 100644 --- a/src/ui/liferea_shell.c +++ b/src/ui/liferea_shell.c @@ -977,7 +977,7 @@ static const GActionEntry liferea_shell_gaction_entries[] = { /* Parameter type must be NULL for toggle. */ {"fullscreen", NULL, NULL, "@b false", on_menu_fullscreen_activate}, - {"reduced-feed-list", NULL, NULL, "@b false", on_feedlist_reduced_activate}, + {"feedlist-view-mode", NULL, "s", "@s 'normal'", on_feedlist_view_mode_activate}, {"toggle-item-read-status", on_toggle_unread_status, "t", NULL, NULL}, {"toggle-item-flag", on_toggle_item_flag, "t", NULL, NULL}, @@ -1280,8 +1280,11 @@ liferea_shell_create (GtkApplication *app, const gchar *overrideWindowState, gin shell->itemlist = itemlist_create (); /* Prepare some toggle button states */ - conf_get_bool_value (REDUCED_FEEDLIST, &toggle); - g_simple_action_set_state ( G_SIMPLE_ACTION (g_action_map_lookup_action (G_ACTION_MAP (app), "reduced-feed-list")), g_variant_new_boolean (toggle)); + + conf_get_enum_value (FEEDLIST_VIEW_MODE, &mode); + g_simple_action_set_state ( + G_SIMPLE_ACTION (g_action_map_lookup_action (G_ACTION_MAP (app), "feedlist-view-mode")), + g_variant_new_string (feed_list_view_mode_value_to_string (mode))); /* Menu creation */ gtk_builder_add_from_file (shell->xml, PACKAGE_DATA_DIR G_DIR_SEPARATOR_S PACKAGE G_DIR_SEPARATOR_S "liferea_menu.ui", NULL); From b33ae43bf3f0c01be0cd77ad10649fb5f4c5661e Mon Sep 17 00:00:00 2001 From: Alexandre Erwin Ittner Date: Sun, 5 Feb 2023 18:36:55 -0300 Subject: [PATCH 3/3] Add a text box to filter feed titles in reduced/flat view modes This is the second part of a two-commit change (actually split after a 'merge --squash'). --- glade/mainwindow.ui | 49 ++++++++++++++++++++++++++++++++--------- src/ui/feed_list_view.c | 29 ++++++++++++++++++++++++ src/ui/feed_list_view.h | 2 ++ 3 files changed, 69 insertions(+), 11 deletions(-) diff --git a/glade/mainwindow.ui b/glade/mainwindow.ui index c59a1c297..63a6742e3 100644 --- a/glade/mainwindow.ui +++ b/glade/mainwindow.ui @@ -29,23 +29,50 @@ True 170 - + True - True - never - in + False + vertical + + + False + True + edit-find-symbolic + False + False + Filter (Alt+g) + + + + False + True + 0 + + - + True True - False - True - - - - + never + in + + + True + True + False + True + + + + + + + + True + 1 + diff --git a/src/ui/feed_list_view.c b/src/ui/feed_list_view.c index eec7966e4..0e76b67c7 100644 --- a/src/ui/feed_list_view.c +++ b/src/ui/feed_list_view.c @@ -48,6 +48,8 @@ struct _FeedListView { GtkTreeView *treeview; GtkTreeModel *filter; GtkTreeStore *feedstore; + GtkSearchEntry *titlefilter; + gchar *casefolded_title_str; GHashTable *flIterHash; /**< hash table used for fast node id <-> tree iter lookup */ @@ -68,6 +70,7 @@ G_DEFINE_TYPE (FeedListView, feed_list_view, G_TYPE_OBJECT); static void feed_list_view_finalize (GObject *object) { + g_free (((FeedListView*)object)->casefolded_title_str); } static void @@ -232,6 +235,15 @@ feed_list_view_filter_visible_function (GtkTreeModel *model, GtkTreeIter *iter, return FALSE; } + if (flv->titlefilter && node->title && flv->casefolded_title_str) { + gchar *text = g_utf8_casefold (node->title, -1); + const gboolean found = strstr (text, flv->casefolded_title_str) ? TRUE : FALSE; + g_free (text); + if (!found) { + return FALSE; + } + } + if (IS_NEWSBIN(node) && node->data && ((feedPtr)node->data)->alwaysShowInReduced) { return TRUE; } @@ -281,10 +293,12 @@ feed_list_view_mode_changed (void) gtk_tree_view_set_reorderable (flv->treeview, FALSE); gtk_tree_view_set_model (flv->treeview, GTK_TREE_MODEL (flv->filter)); gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (flv->filter)); + gtk_widget_show (GTK_WIDGET (flv->titlefilter)); } else { gtk_tree_view_set_reorderable (flv->treeview, TRUE); gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (flv->filter)); gtk_tree_view_set_model (flv->treeview, GTK_TREE_MODEL (flv->feedstore)); + gtk_widget_hide (GTK_WIDGET (flv->titlefilter)); feedlist_foreach (feed_list_view_restore_folder_expansion); } @@ -347,6 +361,8 @@ feed_list_view_create (GtkTreeView *treeview) flv = FEED_LIST_VIEW (g_object_new (FEED_LIST_VIEW_TYPE, NULL)); flv->treeview = treeview; + flv->casefolded_title_str = NULL; + flv->titlefilter = GTK_SEARCH_ENTRY (liferea_shell_lookup ("titleFilter")); flv->feedstore = gtk_tree_store_new (FS_LEN, G_TYPE_STRING, G_TYPE_ICON, @@ -401,6 +417,7 @@ feed_list_view_create (GtkTreeView *treeview) g_signal_connect (G_OBJECT (select), "changed", G_CALLBACK (feed_list_view_selection_changed_cb), flv); + g_signal_connect (GTK_SEARCH_ENTRY (flv->titlefilter), "search-changed", G_CALLBACK (on_titlefilter_entry_changed), flv); conf_get_enum_value (FEEDLIST_VIEW_MODE, (gint *) &flv->view_mode); if (flv->view_mode != FEEDLIST_VIEW_MODE_NORMAL) @@ -578,6 +595,18 @@ on_feedlist_view_mode_activate (GSimpleAction *action, GVariant *parameter, gpoi } } +void +on_titlefilter_entry_changed (GtkEditable *self, gpointer user_data) +{ + if (flv->view_mode == FEEDLIST_VIEW_MODE_REDUCED + || flv->view_mode == FEEDLIST_VIEW_MODE_FLAT) { + g_free(flv->casefolded_title_str); + flv->casefolded_title_str = g_utf8_casefold (gtk_entry_get_text (GTK_ENTRY (flv->titlefilter)), -1); + gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (flv->filter)); + } +} + + // Handling feed list nodes GtkTreeIter * diff --git a/src/ui/feed_list_view.h b/src/ui/feed_list_view.h index bd2fe2b21..e1bf189e8 100644 --- a/src/ui/feed_list_view.h +++ b/src/ui/feed_list_view.h @@ -93,6 +93,8 @@ void on_new_newsbin_activate (GSimpleAction *menuitem, GVariant *parameter, gpoi void on_new_vfolder_activate (GSimpleAction *menuitem, GVariant *parameter, gpointer user_data); void on_feedlist_view_mode_activate (GSimpleAction *action, GVariant *parameter, gpointer user_data); +void on_titlefilter_entry_changed (GtkEditable *self, gpointer user_data); + /** * Determines the tree iter of a given node.