From 8b004b10f60f9cfdf599e53411987b08b110425d Mon Sep 17 00:00:00 2001 From: Ryo Nakano Date: Sun, 13 Oct 2024 13:30:12 +0900 Subject: [PATCH 1/6] Compare content of KeyFile in DesktopFile class --- src/MainWindow.vala | 30 ++++------- src/Model/DesktopFile.vala | 103 ++++++++++++++++++++++--------------- 2 files changed, 72 insertions(+), 61 deletions(-) diff --git a/src/MainWindow.vala b/src/MainWindow.vala index 04db2ee..961ddc4 100644 --- a/src/MainWindow.vala +++ b/src/MainWindow.vala @@ -30,8 +30,6 @@ public class MainWindow : Adw.ApplicationWindow { private Adw.NavigationSplitView split_view; private Model.DesktopFileModel model; - private Model.DesktopFile desktop_file; - private Model.DesktopFile backup_desktop_file; public MainWindow () { Object ( @@ -84,8 +82,6 @@ public class MainWindow : Adw.ApplicationWindow { }); files_view.deleted.connect ((is_success) => { - desktop_file = null; - backup_desktop_file = null; edit_view.hide_all (); model.load.begin (); @@ -102,7 +98,6 @@ public class MainWindow : Adw.ApplicationWindow { }); edit_view.saved.connect (() => { - desktop_file.copy_to (backup_desktop_file); model.load.begin (); var updated_toast = new Adw.Toast (_("Entry updated.")) { @@ -144,18 +139,20 @@ public class MainWindow : Adw.ApplicationWindow { /** * Preprocess before destruction of this. * - * Just destroy this if we never edited entries or no changes made for desktop files. + * Just destroy this if no changes made for desktop files. * Otherwise, tell the user unsaved work through a dialog. */ public void prep_destroy () { - // Never edited entries - if (desktop_file == null || backup_desktop_file == null) { - destroy (); - return; + // Check if there is a desktop file with changes + bool is_changed = false; + foreach (Model.DesktopFile file in model.files_list) { + if (!file.is_clean) { + is_changed = true; + break; + } } - // No changes made - if (desktop_file.equals (backup_desktop_file)) { + if (!is_changed) { destroy (); return; } @@ -200,17 +197,10 @@ public class MainWindow : Adw.ApplicationWindow { /** * Start editing the given {@link Model.DesktopFile}. * - * It first backups the given {@link Model.DesktopFile} and then calls {@link View.EditView.load_file} to start - * editing, so that the app can recognize unsaved changes before destruction. - * * @param file The {@link Model.DesktopFile} to edit */ public void show_edit_view (Model.DesktopFile file) { - desktop_file = file; - backup_desktop_file = new Model.DesktopFile (desktop_file.path); - desktop_file.copy_to (backup_desktop_file); - - edit_view.load_file (desktop_file); + edit_view.load_file (file); split_view.show_content = true; } diff --git a/src/Model/DesktopFile.vala b/src/Model/DesktopFile.vala index e552d5f..75d0e22 100644 --- a/src/Model/DesktopFile.vala +++ b/src/Model/DesktopFile.vala @@ -160,9 +160,22 @@ public class Model.DesktopFile : Object { } /** - * Store data in a single desktop file. + * Returns if this contains unsaved changes to the disk. */ - private KeyFile keyfile; + public bool is_clean { + get { + return equals_keyfile (keyfile_ditry, keyfile_clean); + } + } + + /** + * Data in a single desktop file that loaded from the disk. + */ + private KeyFile keyfile_clean; + /** + * Data in a single desktop file that may contain unsaved changes to the disk. + */ + private KeyFile keyfile_ditry; /** * The constructor. @@ -176,32 +189,42 @@ public class Model.DesktopFile : Object { } construct { - keyfile = new KeyFile (); + keyfile_clean = new KeyFile (); + keyfile_ditry = new KeyFile (); } /** - * Check if this and other contains the same values as desktop files. + * Check if two KeyFiles have the same content. * - * @param other another DesktopFile - * @return true if this and other contains the same values + * @param a KeyFile to compare + * @param b KeyFile to compare + * @return true if two KeyFiles have the same content */ - public bool equals (DesktopFile other) { - // Compare other than the path - string this_data = this.to_data (); - string other_data = other.to_data (); + public bool equals_keyfile (KeyFile a, KeyFile b) { + string a_data = a.to_data (); + string b_data = b.to_data (); - return this_data == other_data; + return a_data == b_data; } /** - * Copy and set data from this to another DesktopFile. + * Sync content of KeyFile between. * - * @param dest another DesktopFile to copy this data to + * @param src KeyFile to be copied from + * @param dst KeyFile to be copied to * @return true if successfully copied, false otherwise */ - public bool copy_to (DesktopFile dest) { - string data = to_data (); - return dest.load_from_data (data); + public bool copy_keyfile (KeyFile dst, KeyFile src) { + string data = src.to_data (); + + try { + dst.load_from_data (data, data.length, KeyFileFlags.KEEP_TRANSLATIONS); + } catch (KeyFileError err) { + warning ("Failed to KeyFile.load_from_data: %s", err.message); + return false; + } + + return true; } //////////////////////////////////////////////////////////////////////////// @@ -225,7 +248,7 @@ public class Model.DesktopFile : Object { } try { - val = keyfile.get_boolean (KeyFileDesktop.GROUP, key); + val = keyfile_ditry.get_boolean (KeyFileDesktop.GROUP, key); } catch (KeyFileError err) { warning ("Failed to KeyFile.get_boolean: key=%s: %s", key, err.message); } @@ -241,7 +264,7 @@ public class Model.DesktopFile : Object { } try { - val = keyfile.get_string (KeyFileDesktop.GROUP, key); + val = keyfile_ditry.get_string (KeyFileDesktop.GROUP, key); } catch (KeyFileError err) { warning ("Failed to KeyFile.get_string: key=%s: %s", key, err.message); } @@ -252,13 +275,13 @@ public class Model.DesktopFile : Object { public bool has_key (string key) { bool ret = false; - // Maybe keyfile is new and has no key yet - if (!keyfile.has_group (KeyFileDesktop.GROUP)) { + // Maybe keyfile_ditry is new and has no key yet + if (!keyfile_ditry.has_group (KeyFileDesktop.GROUP)) { return ret; } try { - ret = keyfile.has_key (KeyFileDesktop.GROUP, key); + ret = keyfile_ditry.has_key (KeyFileDesktop.GROUP, key); } catch (KeyFileError err) { warning ("Failed to KeyFile.has_key: key=%s: %s", key, err.message); } @@ -274,7 +297,7 @@ public class Model.DesktopFile : Object { } try { - val = keyfile.get_string_list (KeyFileDesktop.GROUP, key); + val = keyfile_ditry.get_string_list (KeyFileDesktop.GROUP, key); } catch (KeyFileError err) { warning ("Failed to KeyFile.get_string_list: key=%s: %s", key, err.message); } @@ -285,12 +308,12 @@ public class Model.DesktopFile : Object { public void set_boolean (string key, bool val) { if (val) { // Update the value when the corresponding entry has some value. - keyfile.set_boolean (KeyFileDesktop.GROUP, key, val); + keyfile_ditry.set_boolean (KeyFileDesktop.GROUP, key, val); } else { // Remove the key when it exists and the corresponding entry has no value. if (has_key (key)) { try { - keyfile.remove_key (KeyFileDesktop.GROUP, key); + keyfile_ditry.remove_key (KeyFileDesktop.GROUP, key); } catch (KeyFileError err) { warning ("Failed to KeyFile.remove_key: key=%s: %s", key, err.message); } @@ -301,12 +324,12 @@ public class Model.DesktopFile : Object { public void set_string (string key, string val) { if (val != "") { // Update the value when the corresponding entry has some value. - keyfile.set_string (KeyFileDesktop.GROUP, key, val); + keyfile_ditry.set_string (KeyFileDesktop.GROUP, key, val); } else { // Remove the key when it exists and the corresponding entry has no value. if (has_key (key)) { try { - keyfile.remove_key (KeyFileDesktop.GROUP, key); + keyfile_ditry.remove_key (KeyFileDesktop.GROUP, key); } catch (KeyFileError err) { warning ("Failed to KeyFile.remove_key: key=%s: %s", key, err.message); } @@ -317,12 +340,12 @@ public class Model.DesktopFile : Object { public void set_string_list (string key, string[] list) { if (list.length > 0) { // Update the value when the corresponding entry has some value. - keyfile.set_string_list (KeyFileDesktop.GROUP, key, list); + keyfile_ditry.set_string_list (KeyFileDesktop.GROUP, key, list); } else { // Remove the key when it exists and the corresponding entry has no value. if (has_key (key)) { try { - keyfile.remove_key (KeyFileDesktop.GROUP, key); + keyfile_ditry.remove_key (KeyFileDesktop.GROUP, key); } catch (KeyFileError err) { warning ("Failed to KeyFile.remove_key: key=%s: %s", key, err.message); } @@ -331,14 +354,14 @@ public class Model.DesktopFile : Object { } public string to_data () { - return keyfile.to_data (); + return keyfile_ditry.to_data (); } public bool load_from_data (string data) { bool ret = false; try { - ret = keyfile.load_from_data (data, data.length, KeyFileFlags.KEEP_TRANSLATIONS); + ret = keyfile_ditry.load_from_data (data, data.length, KeyFileFlags.KEEP_TRANSLATIONS); } catch (KeyFileError err) { warning ("Failed to KeyFile.load_from_data: %s", err.message); } @@ -353,7 +376,7 @@ public class Model.DesktopFile : Object { //////////////////////////////////////////////////////////////////////////// public string? get_locale_for_key (string key, string? locale = null) { - return keyfile.get_locale_for_key (KeyFileDesktop.GROUP, key, locale); + return keyfile_ditry.get_locale_for_key (KeyFileDesktop.GROUP, key, locale); } public string get_locale_string (string key, string? locale = null) { @@ -364,7 +387,7 @@ public class Model.DesktopFile : Object { } try { - val = keyfile.get_locale_string (KeyFileDesktop.GROUP, key, locale); + val = keyfile_ditry.get_locale_string (KeyFileDesktop.GROUP, key, locale); } catch (KeyFileError err) { warning ("Failed to KeyFile.get_locale_string: key=%s: %s", key, err.message); } @@ -379,29 +402,27 @@ public class Model.DesktopFile : Object { //////////////////////////////////////////////////////////////////////////// public bool load_file () { - bool ret = false; - try { - ret = keyfile.load_from_file (path, KeyFileFlags.KEEP_TRANSLATIONS); + keyfile_clean.load_from_file (path, KeyFileFlags.KEEP_TRANSLATIONS); } catch (FileError err) { warning ("Failed to load from file. path=%s: %s", path, err.message); + return false; } catch (KeyFileError err) { - warning ("Invalid keyfile. path=%s: %s", path, err.message); + warning ("Invalid keyfile_ditry. path=%s: %s", path, err.message); + return false; } - return ret; + return copy_keyfile (keyfile_ditry, keyfile_clean); } public bool save_file () { - bool ret = false; - try { - ret = keyfile.save_to_file (path); + keyfile_ditry.save_to_file (path); } catch (FileError err) { warning ("Failed to save file. path=%s: %s", path, err.message); } - return ret; + return copy_keyfile (keyfile_clean, keyfile_ditry); } public bool delete_file () { From 5bbfa4a4dcc79c183399cf1c4d0df445ffe582fb Mon Sep 17 00:00:00 2001 From: Ryo Nakano Date: Sun, 13 Oct 2024 14:09:06 +0900 Subject: [PATCH 2/6] Introduce KeyFileUtil Some methods in DesktopFile are just wrappers of KeyFile and not related to DesktopFile itself. --- src/Model/DesktopFile.vala | 239 +++++-------------------------------- src/Util/KeyFileUtil.vala | 210 ++++++++++++++++++++++++++++++++ src/View/EditView.vala | 2 +- src/meson.build | 1 + 4 files changed, 243 insertions(+), 209 deletions(-) create mode 100644 src/Util/KeyFileUtil.vala diff --git a/src/Model/DesktopFile.vala b/src/Model/DesktopFile.vala index e552d5f..4e6a4fb 100644 --- a/src/Model/DesktopFile.vala +++ b/src/Model/DesktopFile.vala @@ -13,39 +13,32 @@ public class Model.DesktopFile : Object { public const string DESKTOP_SUFFIX = ".desktop"; /** - * A key under ``g_key_file_desktop_group``, whose value is a list of strings giving the keywords which may be used in - * addition to other metadata to describe this entry. - * - * Note: Using ``KeyFileDesktop.KEY_KEYWORDS`` will cause the cc failing with ``‘G_KEY_FILE_DESKTOP_KEY_KEYWORDS’ undeclared`` - * error.<
> - * This constant does not seem to be defined in the original glib and defined in the following patch.<
> - * (and maybe valac uses glibc with this patch and thus it does not complain any error.)<
> - * <
> - * [[https://sources.debian.org/patches/glib2.0/2.78.3-2/01_gettext-desktopfiles.patch/]]<
> - * <
> - * We just keep to borrow the definition of KEY_KEYWORDS here instead of applying the patch, - * since it might have side effect. + * The absolute path to the desktop file. */ - public const string KEY_KEYWORDS = "Keywords"; + public string path { get; construct; } /** - * The absolute path to the desktop file. + * Value of "Type" entry. */ - public string path { get; construct; } + public string value_type { + set { + Util.KeyFileUtil.set_string (keyfile, KeyFileDesktop.KEY_TYPE, value); + } + } /** * Value of "Name" entry. */ public string value_name { owned get { - string? locale = get_locale_for_key (KeyFileDesktop.KEY_NAME, Application.preferred_language); - string name = get_locale_string (KeyFileDesktop.KEY_NAME, locale); + string? locale = Util.KeyFileUtil.get_locale_for_key (keyfile, KeyFileDesktop.KEY_NAME, Application.preferred_language); + string name = Util.KeyFileUtil.get_locale_string (keyfile, KeyFileDesktop.KEY_NAME, locale); return name; } set { - set_string (KeyFileDesktop.KEY_NAME, value); + Util.KeyFileUtil.set_string (keyfile, KeyFileDesktop.KEY_NAME, value); } } @@ -54,11 +47,11 @@ public class Model.DesktopFile : Object { */ public string value_exec { owned get { - return get_string (KeyFileDesktop.KEY_EXEC); + return Util.KeyFileUtil.get_string (keyfile, KeyFileDesktop.KEY_EXEC); } set { - set_string (KeyFileDesktop.KEY_EXEC, value); + Util.KeyFileUtil.set_string (keyfile, KeyFileDesktop.KEY_EXEC, value); } } @@ -67,11 +60,11 @@ public class Model.DesktopFile : Object { */ public string value_icon { owned get { - return get_string (KeyFileDesktop.KEY_ICON); + return Util.KeyFileUtil.get_string (keyfile, KeyFileDesktop.KEY_ICON); } set { - set_string (KeyFileDesktop.KEY_ICON, value); + Util.KeyFileUtil.set_string (keyfile, KeyFileDesktop.KEY_ICON, value); } } @@ -80,14 +73,14 @@ public class Model.DesktopFile : Object { */ public string value_generic_name { owned get { - string? locale = get_locale_for_key (KeyFileDesktop.KEY_GENERIC_NAME, Application.preferred_language); - string generic_name = get_locale_string (KeyFileDesktop.KEY_GENERIC_NAME, locale); + string? locale = Util.KeyFileUtil.get_locale_for_key (keyfile, KeyFileDesktop.KEY_GENERIC_NAME, Application.preferred_language); + string generic_name = Util.KeyFileUtil.get_locale_string (keyfile, KeyFileDesktop.KEY_GENERIC_NAME, locale); return generic_name; } set { - set_string (KeyFileDesktop.KEY_GENERIC_NAME, value); + Util.KeyFileUtil.set_string (keyfile, KeyFileDesktop.KEY_GENERIC_NAME, value); } } @@ -96,14 +89,14 @@ public class Model.DesktopFile : Object { */ public string value_comment { owned get { - string? locale = get_locale_for_key (KeyFileDesktop.KEY_COMMENT, Application.preferred_language); - string comment = get_locale_string (KeyFileDesktop.KEY_COMMENT, locale); + string? locale = Util.KeyFileUtil.get_locale_for_key (keyfile, KeyFileDesktop.KEY_COMMENT, Application.preferred_language); + string comment = Util.KeyFileUtil.get_locale_string (keyfile, KeyFileDesktop.KEY_COMMENT, locale); return comment; } set { - set_string (KeyFileDesktop.KEY_COMMENT, value); + Util.KeyFileUtil.set_string (keyfile, KeyFileDesktop.KEY_COMMENT, value); } } @@ -112,11 +105,11 @@ public class Model.DesktopFile : Object { */ public string[] value_categories { owned get { - return get_string_list (KeyFileDesktop.KEY_CATEGORIES); + return Util.KeyFileUtil.get_string_list (keyfile, KeyFileDesktop.KEY_CATEGORIES); } set { - set_string_list (KeyFileDesktop.KEY_CATEGORIES, value); + Util.KeyFileUtil.set_string_list (keyfile, KeyFileDesktop.KEY_CATEGORIES, value); } } @@ -125,11 +118,11 @@ public class Model.DesktopFile : Object { */ public string[] value_keywords { owned get { - return get_string_list (KEY_KEYWORDS); + return Util.KeyFileUtil.get_string_list (keyfile, Util.KeyFileUtil.KEY_KEYWORDS); } set { - set_string_list (KEY_KEYWORDS, value); + Util.KeyFileUtil.set_string_list (keyfile, Util.KeyFileUtil.KEY_KEYWORDS, value); } } @@ -138,11 +131,11 @@ public class Model.DesktopFile : Object { */ public string value_startup_wm_class { owned get { - return get_string (KeyFileDesktop.KEY_STARTUP_WM_CLASS); + return Util.KeyFileUtil.get_string (keyfile, KeyFileDesktop.KEY_STARTUP_WM_CLASS); } set { - set_string (KeyFileDesktop.KEY_STARTUP_WM_CLASS, value); + Util.KeyFileUtil.set_string (keyfile, KeyFileDesktop.KEY_STARTUP_WM_CLASS, value); } } @@ -151,11 +144,11 @@ public class Model.DesktopFile : Object { */ public bool value_terminal { get { - return get_boolean (KeyFileDesktop.KEY_TERMINAL); + return Util.KeyFileUtil.get_boolean (keyfile, KeyFileDesktop.KEY_TERMINAL); } set { - set_boolean (KeyFileDesktop.KEY_TERMINAL, value); + Util.KeyFileUtil.set_boolean (keyfile, KeyFileDesktop.KEY_TERMINAL, value); } } @@ -204,132 +197,6 @@ public class Model.DesktopFile : Object { return dest.load_from_data (data); } - //////////////////////////////////////////////////////////////////////////// - // - // Key Opearations - // - //////////////////////////////////////////////////////////////////////////// - - /** - * Return the value associated with ``key`` as a boolean. - * - * @param key a key - * @return the value associated with the key as a boolean, or false if the key was not found or could not be parsed - * @see GLib.KeyFile.get_boolean - */ - public bool get_boolean (string key) { - bool val = false; - - if (!has_key (key)) { - return val; - } - - try { - val = keyfile.get_boolean (KeyFileDesktop.GROUP, key); - } catch (KeyFileError err) { - warning ("Failed to KeyFile.get_boolean: key=%s: %s", key, err.message); - } - - return val; - } - - public string get_string (string key) { - string val = ""; - - if (!has_key (key)) { - return val; - } - - try { - val = keyfile.get_string (KeyFileDesktop.GROUP, key); - } catch (KeyFileError err) { - warning ("Failed to KeyFile.get_string: key=%s: %s", key, err.message); - } - - return val; - } - - public bool has_key (string key) { - bool ret = false; - - // Maybe keyfile is new and has no key yet - if (!keyfile.has_group (KeyFileDesktop.GROUP)) { - return ret; - } - - try { - ret = keyfile.has_key (KeyFileDesktop.GROUP, key); - } catch (KeyFileError err) { - warning ("Failed to KeyFile.has_key: key=%s: %s", key, err.message); - } - - return ret; - } - - public string[] get_string_list (string key) { - string[] val = {}; - - if (!has_key (key)) { - return val; - } - - try { - val = keyfile.get_string_list (KeyFileDesktop.GROUP, key); - } catch (KeyFileError err) { - warning ("Failed to KeyFile.get_string_list: key=%s: %s", key, err.message); - } - - return val; - } - - public void set_boolean (string key, bool val) { - if (val) { - // Update the value when the corresponding entry has some value. - keyfile.set_boolean (KeyFileDesktop.GROUP, key, val); - } else { - // Remove the key when it exists and the corresponding entry has no value. - if (has_key (key)) { - try { - keyfile.remove_key (KeyFileDesktop.GROUP, key); - } catch (KeyFileError err) { - warning ("Failed to KeyFile.remove_key: key=%s: %s", key, err.message); - } - } - } - } - - public void set_string (string key, string val) { - if (val != "") { - // Update the value when the corresponding entry has some value. - keyfile.set_string (KeyFileDesktop.GROUP, key, val); - } else { - // Remove the key when it exists and the corresponding entry has no value. - if (has_key (key)) { - try { - keyfile.remove_key (KeyFileDesktop.GROUP, key); - } catch (KeyFileError err) { - warning ("Failed to KeyFile.remove_key: key=%s: %s", key, err.message); - } - } - } - } - - public void set_string_list (string key, string[] list) { - if (list.length > 0) { - // Update the value when the corresponding entry has some value. - keyfile.set_string_list (KeyFileDesktop.GROUP, key, list); - } else { - // Remove the key when it exists and the corresponding entry has no value. - if (has_key (key)) { - try { - keyfile.remove_key (KeyFileDesktop.GROUP, key); - } catch (KeyFileError err) { - warning ("Failed to KeyFile.remove_key: key=%s: %s", key, err.message); - } - } - } - } - public string to_data () { return keyfile.to_data (); } @@ -346,32 +213,6 @@ public class Model.DesktopFile : Object { return ret; } - //////////////////////////////////////////////////////////////////////////// - // - // Localized Key Opearations - // - //////////////////////////////////////////////////////////////////////////// - - public string? get_locale_for_key (string key, string? locale = null) { - return keyfile.get_locale_for_key (KeyFileDesktop.GROUP, key, locale); - } - - public string get_locale_string (string key, string? locale = null) { - string val = ""; - - if (!has_key (key)) { - return val; - } - - try { - val = keyfile.get_locale_string (KeyFileDesktop.GROUP, key, locale); - } catch (KeyFileError err) { - warning ("Failed to KeyFile.get_locale_string: key=%s: %s", key, err.message); - } - - return val; - } - //////////////////////////////////////////////////////////////////////////// // // File Opearations @@ -379,29 +220,11 @@ public class Model.DesktopFile : Object { //////////////////////////////////////////////////////////////////////////// public bool load_file () { - bool ret = false; - - try { - ret = keyfile.load_from_file (path, KeyFileFlags.KEEP_TRANSLATIONS); - } catch (FileError err) { - warning ("Failed to load from file. path=%s: %s", path, err.message); - } catch (KeyFileError err) { - warning ("Invalid keyfile. path=%s: %s", path, err.message); - } - - return ret; + return Util.KeyFileUtil.load_file (keyfile, path, KeyFileFlags.KEEP_TRANSLATIONS); } public bool save_file () { - bool ret = false; - - try { - ret = keyfile.save_to_file (path); - } catch (FileError err) { - warning ("Failed to save file. path=%s: %s", path, err.message); - } - - return ret; + return Util.KeyFileUtil.save_file (keyfile, path); } public bool delete_file () { diff --git a/src/Util/KeyFileUtil.vala b/src/Util/KeyFileUtil.vala new file mode 100644 index 0000000..8036223 --- /dev/null +++ b/src/Util/KeyFileUtil.vala @@ -0,0 +1,210 @@ +/* + * SPDX-License-Identifier: GPL-3.0-or-later + * SPDX-FileCopyrightText: 2021-2024 Ryo Nakano + */ + +/** + * Definitions and wrapper methods related to KeyFile. + */ +namespace Util.KeyFileUtil { + /** + * A key under ``g_key_file_desktop_group``, whose value is a list of strings giving the keywords which may be used in + * addition to other metadata to describe this entry. + * + * Note: Using ``KeyFileDesktop.KEY_KEYWORDS`` will cause the cc failing with ``‘G_KEY_FILE_DESKTOP_KEY_KEYWORDS’ undeclared`` + * error.<
> + * This constant does not seem to be defined in the original glib and defined in the following patch.<
> + * (and maybe valac uses glibc with this patch and thus it does not complain any error.)<
> + * <
> + * [[https://sources.debian.org/patches/glib2.0/2.78.3-2/01_gettext-desktopfiles.patch/]]<
> + * <
> + * We just keep to borrow the definition of KEY_KEYWORDS here instead of applying the patch, + * since it might have side effect. + */ + public const string KEY_KEYWORDS = "Keywords"; + + //////////////////////////////////////////////////////////////////////////// + // + // Key Opearations + // + //////////////////////////////////////////////////////////////////////////// + + /** + * Return the value associated with ``key`` as a boolean. + * + * @param keyfile a KeyFile + * @param key a key + * @return the value associated with the key as a boolean, or false if the key was not found or could not be parsed + * @see GLib.KeyFile.get_boolean + */ + public static bool get_boolean (KeyFile keyfile, string key) { + bool val = false; + + if (!has_key (keyfile, key)) { + return val; + } + + try { + val = keyfile.get_boolean (KeyFileDesktop.GROUP, key); + } catch (KeyFileError err) { + warning ("Failed to KeyFile.get_boolean: key=%s: %s", key, err.message); + } + + return val; + } + + public static string get_string (KeyFile keyfile, string key) { + string val = ""; + + if (!has_key (keyfile, key)) { + return val; + } + + try { + val = keyfile.get_string (KeyFileDesktop.GROUP, key); + } catch (KeyFileError err) { + warning ("Failed to KeyFile.get_string: key=%s: %s", key, err.message); + } + + return val; + } + + public static bool has_key (KeyFile keyfile, string key) { + bool ret = false; + + // Maybe keyfile is new and has no key yet + if (!keyfile.has_group (KeyFileDesktop.GROUP)) { + return ret; + } + + try { + ret = keyfile.has_key (KeyFileDesktop.GROUP, key); + } catch (KeyFileError err) { + warning ("Failed to KeyFile.has_key: key=%s: %s", key, err.message); + } + + return ret; + } + + public static string[] get_string_list (KeyFile keyfile, string key) { + string[] val = {}; + + if (!has_key (keyfile, key)) { + return val; + } + + try { + val = keyfile.get_string_list (KeyFileDesktop.GROUP, key); + } catch (KeyFileError err) { + warning ("Failed to KeyFile.get_string_list: key=%s: %s", key, err.message); + } + + return val; + } + + public static void set_boolean (KeyFile keyfile, string key, bool val) { + if (val) { + // Update the value when the corresponding entry has some value. + keyfile.set_boolean (KeyFileDesktop.GROUP, key, val); + } else { + // Remove the key when it exists and the corresponding entry has no value. + if (has_key (keyfile, key)) { + try { + keyfile.remove_key (KeyFileDesktop.GROUP, key); + } catch (KeyFileError err) { + warning ("Failed to KeyFile.remove_key: key=%s: %s", key, err.message); + } + } + } + } + + public static void set_string (KeyFile keyfile, string key, string val) { + if (val != "") { + // Update the value when the corresponding entry has some value. + keyfile.set_string (KeyFileDesktop.GROUP, key, val); + } else { + // Remove the key when it exists and the corresponding entry has no value. + if (has_key (keyfile, key)) { + try { + keyfile.remove_key (KeyFileDesktop.GROUP, key); + } catch (KeyFileError err) { + warning ("Failed to KeyFile.remove_key: key=%s: %s", key, err.message); + } + } + } + } + + public static void set_string_list (KeyFile keyfile, string key, string[] list) { + if (list.length > 0) { + // Update the value when the corresponding entry has some value. + keyfile.set_string_list (KeyFileDesktop.GROUP, key, list); + } else { + // Remove the key when it exists and the corresponding entry has no value. + if (has_key (keyfile, key)) { + try { + keyfile.remove_key (KeyFileDesktop.GROUP, key); + } catch (KeyFileError err) { + warning ("Failed to KeyFile.remove_key: key=%s: %s", key, err.message); + } + } + } + } + + //////////////////////////////////////////////////////////////////////////// + // + // Localized Key Opearations + // + //////////////////////////////////////////////////////////////////////////// + + public static string? get_locale_for_key (KeyFile keyfile, string key, string? locale = null) { + return keyfile.get_locale_for_key (KeyFileDesktop.GROUP, key, locale); + } + + public static string get_locale_string (KeyFile keyfile, string key, string? locale = null) { + string val = ""; + + if (!has_key (keyfile, key)) { + return val; + } + + try { + val = keyfile.get_locale_string (KeyFileDesktop.GROUP, key, locale); + } catch (KeyFileError err) { + warning ("Failed to KeyFile.get_locale_string: key=%s: %s", key, err.message); + } + + return val; + } + + //////////////////////////////////////////////////////////////////////////// + // + // File Opearations + // + //////////////////////////////////////////////////////////////////////////// + + public static bool load_file (KeyFile keyfile, string path, KeyFileFlags flags) { + bool ret = false; + + try { + ret = keyfile.load_from_file (path, flags); + } catch (FileError err) { + warning ("Failed to load from file. path=%s: %s", path, err.message); + } catch (KeyFileError err) { + warning ("Invalid keyfile. path=%s: %s", path, err.message); + } + + return ret; + } + + public static bool save_file (KeyFile keyfile, string path) { + bool ret = false; + + try { + ret = keyfile.save_to_file (path); + } catch (FileError err) { + warning ("Failed to save file. path=%s: %s", path, err.message); + } + + return ret; + } +} diff --git a/src/View/EditView.vala b/src/View/EditView.vala index 09731d5..a0fcad4 100644 --- a/src/View/EditView.vala +++ b/src/View/EditView.vala @@ -451,7 +451,7 @@ public class View.EditView : Adw.NavigationPage { * Get values from the input widgets in the view and save them to a desktop file. */ public void save_file () { - desktop_file.set_string (KeyFileDesktop.KEY_TYPE, "Application"); + desktop_file.value_type = "Application"; bool ret = desktop_file.save_file (); diff --git a/src/meson.build b/src/meson.build index b28702e..5b6b53b 100644 --- a/src/meson.build +++ b/src/meson.build @@ -29,6 +29,7 @@ dependencies = [ sources = files( 'Model/DesktopFile.vala', 'Model/DesktopFileModel.vala', + 'Util/KeyFileUtil.vala', 'Util/DesktopFileUtil.vala', 'View/EditView.vala', 'View/FilesView.vala', From 2fc5790d6dbc55f1ee21ee8180179ae46ef08eb1 Mon Sep 17 00:00:00 2001 From: Ryo Nakano Date: Sun, 13 Oct 2024 14:13:32 +0900 Subject: [PATCH 3/6] Remove unused methods --- src/Model/DesktopFile.vala | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/src/Model/DesktopFile.vala b/src/Model/DesktopFile.vala index 75d0e22..ec034df 100644 --- a/src/Model/DesktopFile.vala +++ b/src/Model/DesktopFile.vala @@ -353,22 +353,6 @@ public class Model.DesktopFile : Object { } } - public string to_data () { - return keyfile_ditry.to_data (); - } - - public bool load_from_data (string data) { - bool ret = false; - - try { - ret = keyfile_ditry.load_from_data (data, data.length, KeyFileFlags.KEEP_TRANSLATIONS); - } catch (KeyFileError err) { - warning ("Failed to KeyFile.load_from_data: %s", err.message); - } - - return ret; - } - //////////////////////////////////////////////////////////////////////////// // // Localized Key Opearations From e8cd322cd419849fd5b32455fc05c90a9a9f9d18 Mon Sep 17 00:00:00 2001 From: Ryo Nakano Date: Sun, 13 Oct 2024 14:17:29 +0900 Subject: [PATCH 4/6] Fix typo --- src/Model/DesktopFile.vala | 42 +++++++++++++++++++------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/src/Model/DesktopFile.vala b/src/Model/DesktopFile.vala index ec034df..b722c57 100644 --- a/src/Model/DesktopFile.vala +++ b/src/Model/DesktopFile.vala @@ -164,7 +164,7 @@ public class Model.DesktopFile : Object { */ public bool is_clean { get { - return equals_keyfile (keyfile_ditry, keyfile_clean); + return equals_keyfile (keyfile_dirty, keyfile_clean); } } @@ -175,7 +175,7 @@ public class Model.DesktopFile : Object { /** * Data in a single desktop file that may contain unsaved changes to the disk. */ - private KeyFile keyfile_ditry; + private KeyFile keyfile_dirty; /** * The constructor. @@ -190,7 +190,7 @@ public class Model.DesktopFile : Object { construct { keyfile_clean = new KeyFile (); - keyfile_ditry = new KeyFile (); + keyfile_dirty = new KeyFile (); } /** @@ -248,7 +248,7 @@ public class Model.DesktopFile : Object { } try { - val = keyfile_ditry.get_boolean (KeyFileDesktop.GROUP, key); + val = keyfile_dirty.get_boolean (KeyFileDesktop.GROUP, key); } catch (KeyFileError err) { warning ("Failed to KeyFile.get_boolean: key=%s: %s", key, err.message); } @@ -264,7 +264,7 @@ public class Model.DesktopFile : Object { } try { - val = keyfile_ditry.get_string (KeyFileDesktop.GROUP, key); + val = keyfile_dirty.get_string (KeyFileDesktop.GROUP, key); } catch (KeyFileError err) { warning ("Failed to KeyFile.get_string: key=%s: %s", key, err.message); } @@ -275,13 +275,13 @@ public class Model.DesktopFile : Object { public bool has_key (string key) { bool ret = false; - // Maybe keyfile_ditry is new and has no key yet - if (!keyfile_ditry.has_group (KeyFileDesktop.GROUP)) { + // Maybe keyfile_dirty is new and has no key yet + if (!keyfile_dirty.has_group (KeyFileDesktop.GROUP)) { return ret; } try { - ret = keyfile_ditry.has_key (KeyFileDesktop.GROUP, key); + ret = keyfile_dirty.has_key (KeyFileDesktop.GROUP, key); } catch (KeyFileError err) { warning ("Failed to KeyFile.has_key: key=%s: %s", key, err.message); } @@ -297,7 +297,7 @@ public class Model.DesktopFile : Object { } try { - val = keyfile_ditry.get_string_list (KeyFileDesktop.GROUP, key); + val = keyfile_dirty.get_string_list (KeyFileDesktop.GROUP, key); } catch (KeyFileError err) { warning ("Failed to KeyFile.get_string_list: key=%s: %s", key, err.message); } @@ -308,12 +308,12 @@ public class Model.DesktopFile : Object { public void set_boolean (string key, bool val) { if (val) { // Update the value when the corresponding entry has some value. - keyfile_ditry.set_boolean (KeyFileDesktop.GROUP, key, val); + keyfile_dirty.set_boolean (KeyFileDesktop.GROUP, key, val); } else { // Remove the key when it exists and the corresponding entry has no value. if (has_key (key)) { try { - keyfile_ditry.remove_key (KeyFileDesktop.GROUP, key); + keyfile_dirty.remove_key (KeyFileDesktop.GROUP, key); } catch (KeyFileError err) { warning ("Failed to KeyFile.remove_key: key=%s: %s", key, err.message); } @@ -324,12 +324,12 @@ public class Model.DesktopFile : Object { public void set_string (string key, string val) { if (val != "") { // Update the value when the corresponding entry has some value. - keyfile_ditry.set_string (KeyFileDesktop.GROUP, key, val); + keyfile_dirty.set_string (KeyFileDesktop.GROUP, key, val); } else { // Remove the key when it exists and the corresponding entry has no value. if (has_key (key)) { try { - keyfile_ditry.remove_key (KeyFileDesktop.GROUP, key); + keyfile_dirty.remove_key (KeyFileDesktop.GROUP, key); } catch (KeyFileError err) { warning ("Failed to KeyFile.remove_key: key=%s: %s", key, err.message); } @@ -340,12 +340,12 @@ public class Model.DesktopFile : Object { public void set_string_list (string key, string[] list) { if (list.length > 0) { // Update the value when the corresponding entry has some value. - keyfile_ditry.set_string_list (KeyFileDesktop.GROUP, key, list); + keyfile_dirty.set_string_list (KeyFileDesktop.GROUP, key, list); } else { // Remove the key when it exists and the corresponding entry has no value. if (has_key (key)) { try { - keyfile_ditry.remove_key (KeyFileDesktop.GROUP, key); + keyfile_dirty.remove_key (KeyFileDesktop.GROUP, key); } catch (KeyFileError err) { warning ("Failed to KeyFile.remove_key: key=%s: %s", key, err.message); } @@ -360,7 +360,7 @@ public class Model.DesktopFile : Object { //////////////////////////////////////////////////////////////////////////// public string? get_locale_for_key (string key, string? locale = null) { - return keyfile_ditry.get_locale_for_key (KeyFileDesktop.GROUP, key, locale); + return keyfile_dirty.get_locale_for_key (KeyFileDesktop.GROUP, key, locale); } public string get_locale_string (string key, string? locale = null) { @@ -371,7 +371,7 @@ public class Model.DesktopFile : Object { } try { - val = keyfile_ditry.get_locale_string (KeyFileDesktop.GROUP, key, locale); + val = keyfile_dirty.get_locale_string (KeyFileDesktop.GROUP, key, locale); } catch (KeyFileError err) { warning ("Failed to KeyFile.get_locale_string: key=%s: %s", key, err.message); } @@ -392,21 +392,21 @@ public class Model.DesktopFile : Object { warning ("Failed to load from file. path=%s: %s", path, err.message); return false; } catch (KeyFileError err) { - warning ("Invalid keyfile_ditry. path=%s: %s", path, err.message); + warning ("Invalid keyfile_dirty. path=%s: %s", path, err.message); return false; } - return copy_keyfile (keyfile_ditry, keyfile_clean); + return copy_keyfile (keyfile_dirty, keyfile_clean); } public bool save_file () { try { - keyfile_ditry.save_to_file (path); + keyfile_dirty.save_to_file (path); } catch (FileError err) { warning ("Failed to save file. path=%s: %s", path, err.message); } - return copy_keyfile (keyfile_clean, keyfile_ditry); + return copy_keyfile (keyfile_clean, keyfile_dirty); } public bool delete_file () { From 603be239e8febcb2a490f3a03f83b81f50ccd503 Mon Sep 17 00:00:00 2001 From: Ryo Nakano Date: Sun, 13 Oct 2024 16:26:54 +0900 Subject: [PATCH 5/6] Access to disk only when saved or deleted Re-loading DesktopFileModel.files_list changes addresses of DesktopFile in the list and causes the app crash when continuing editing. --- src/MainWindow.vala | 59 +++++++++++++++++---------------- src/Model/DesktopFile.vala | 13 -------- src/Model/DesktopFileModel.vala | 59 +++++++++++++++++++++++++++++++++ src/View/FilesView.vala | 28 ++++------------ 4 files changed, 96 insertions(+), 63 deletions(-) diff --git a/src/MainWindow.vala b/src/MainWindow.vala index 961ddc4..6bb7dec 100644 --- a/src/MainWindow.vala +++ b/src/MainWindow.vala @@ -9,12 +9,8 @@ * It contains a ``Adw.NavigationSplitView`` as a view which has a {@link View.FilesView} and * {@link View.EditView} inside. * - * It also has a instance of {@link Model.DesktopFileModel} and calls {@link Model.DesktopFileModel.load} when: - * - * * constructed - * * {@link View.FilesView.deleted} is emitted - * * {@link View.EditView.saved} is emitted - * * the action ``win.new`` is called + * It also has a instance of {@link Model.DesktopFileModel} and calls {@link Model.DesktopFileModel.load} when + * constructed. * * If {@link Model.DesktopFileModel.load} succeeded, this calls {@link View.FilesView.set_list_data} to reflect * load result.<
> @@ -81,16 +77,32 @@ public class MainWindow : Adw.ApplicationWindow { on_new_activate (); }); - files_view.deleted.connect ((is_success) => { + files_view.delete_activated.connect ((file) => { edit_view.hide_all (); - model.load.begin (); - if (is_success) { - var deleted_toast = new Adw.Toast (_("Entry deleted.")) { - timeout = 5 - }; - overlay.add_toast (deleted_toast); + bool ret = model.delete_file (file); + if (!ret) { + var error_dialog = new Adw.MessageDialog ( + (Gtk.Window) get_root (), + _("Failed to Delete Entry of “%s”").printf (file.value_name), + _("There was an error while removing the app entry.") + ); + + error_dialog.add_response (Define.DialogResponse.CLOSE, _("_Close")); + + error_dialog.default_response = Define.DialogResponse.CLOSE; + error_dialog.close_response = Define.DialogResponse.CLOSE; + + error_dialog.present (); + return; } + + show_files_view (); + + var deleted_toast = new Adw.Toast (_("Entry deleted.")) { + timeout = 5 + }; + overlay.add_toast (deleted_toast); }); files_view.selected.connect ((entry) => { @@ -98,7 +110,7 @@ public class MainWindow : Adw.ApplicationWindow { }); edit_view.saved.connect (() => { - model.load.begin (); + files_view.set_list_data (model.files_list); var updated_toast = new Adw.Toast (_("Entry updated.")) { timeout = 5 @@ -190,7 +202,7 @@ public class MainWindow : Adw.ApplicationWindow { * Reload and show the file list. */ public void show_files_view () { - model.load.begin (); + files_view.set_list_data (model.files_list); split_view.show_content = false; } @@ -207,23 +219,12 @@ public class MainWindow : Adw.ApplicationWindow { /** * The callback for new file. * - * Create a new DesktopFile with random filename and start editing it. + * Create a new DesktopFile and start editing it. */ private void on_new_activate () { - string filename = Config.APP_ID + "." + Uuid.string_random (); - string path = Path.build_filename ( - Environment.get_home_dir (), ".local/share/applications", - filename + Model.DesktopFile.DESKTOP_SUFFIX - ); + Model.DesktopFile file = model.create_file (); - var file = new Model.DesktopFile (path); - - bool ret = file.save_file (); - if (!ret) { - return; - } - - model.load.begin (); + files_view.set_list_data (model.files_list); show_edit_view (file); } } diff --git a/src/Model/DesktopFile.vala b/src/Model/DesktopFile.vala index 3a4c610..7317610 100644 --- a/src/Model/DesktopFile.vala +++ b/src/Model/DesktopFile.vala @@ -209,17 +209,4 @@ public class Model.DesktopFile : Object { return Util.KeyFileUtil.copy (keyfile_clean, keyfile_dirty); } - - public bool delete_file () { - var file = File.new_for_path (path); - bool ret = false; - - try { - ret = file.delete (); - } catch (Error err) { - warning ("Failed to delete file. path=%s: %s", path, err.message); - } - - return ret; - } } diff --git a/src/Model/DesktopFileModel.vala b/src/Model/DesktopFileModel.vala index 0abe4ab..a6ebc6b 100644 --- a/src/Model/DesktopFileModel.vala +++ b/src/Model/DesktopFileModel.vala @@ -120,4 +120,63 @@ public class Model.DesktopFileModel : Object { load_failure (); } } + + /** + * Create a new {@link DesktopFile} with random filename. + * + * @return Created {@link DesktopFile} + */ + public Model.DesktopFile create_file () { + string filename = Config.APP_ID + "." + Uuid.string_random (); + string path = Path.build_filename ( + desktop_files_path, + filename + Model.DesktopFile.DESKTOP_SUFFIX + ); + + var file = new Model.DesktopFile (path); + + files_list.add (file); + + return file; + } + + /** + * Delete desktop file from list and the disk. + * + * @param file A {@link DesktopFile} to delete + * @return true if succeeded, false otherwise + */ + public bool delete_file (Model.DesktopFile file) { + bool ret = delete_from_disk (file.path); + if (!ret) { + return false; + } + + files_list.remove (file); + + return true; + } + + /** + * Delete file at path from the disk. + * + * @param path A file to delete + * @return true if succeeded, false otherwise + */ + private bool delete_from_disk (string path) { + var file = File.new_for_path (path); + + if (!file.query_exists ()) { + return true; + } + + bool ret = false; + try { + ret = file.delete (); + } catch (Error err) { + warning ("Failed to delete file. path=%s: %s", path, err.message); + } + + return ret; + } } diff --git a/src/View/FilesView.vala b/src/View/FilesView.vala index 59d1c3e..2c91069 100644 --- a/src/View/FilesView.vala +++ b/src/View/FilesView.vala @@ -5,7 +5,7 @@ public class View.FilesView : Adw.NavigationPage { public signal void new_activated (); - public signal void deleted (bool is_success); + public signal void delete_activated (Model.DesktopFile file); public signal void selected (Model.DesktopFile file); private ListStore list_store; @@ -106,6 +106,8 @@ public class View.FilesView : Adw.NavigationPage { private Gtk.Widget create_files_row (Object item) { var file = item as Model.DesktopFile; + string app_name = file.value_name; + var app_icon = new Gtk.Image.from_gicon (new ThemedIcon ("application-x-executable")) { pixel_size = 48, margin_top = 6, @@ -123,39 +125,23 @@ public class View.FilesView : Adw.NavigationPage { }; delete_button.add_css_class ("flat"); delete_button.clicked.connect (() => { - var delete_dialog = setup_delete_dialog (file.value_name); + var delete_dialog = setup_delete_dialog (app_name); delete_dialog.response.connect ((response_id) => { if (response_id != Define.DialogResponse.OK) { return; } - bool ret = file.delete_file (); - if (!ret) { - var error_dialog = new Adw.MessageDialog ( - (Gtk.Window) get_root (), - _("Failed to Delete Entry of “%s”").printf (file.value_name), - _("There was an error while removing the app entry.") - ); - - error_dialog.add_response (Define.DialogResponse.CLOSE, _("_Close")); - - error_dialog.default_response = Define.DialogResponse.CLOSE; - error_dialog.close_response = Define.DialogResponse.CLOSE; - - error_dialog.present (); - } - - deleted (ret); - delete_dialog.destroy (); + + delete_activated (file); }); delete_dialog.present (); }); var row = new Adw.ActionRow () { - title = file.value_name, + title = app_name, subtitle = file.value_comment, title_lines = 1, subtitle_lines = 1, From 076f5bb7f6b70d9c1188bcd6e83556a16895905a Mon Sep 17 00:00:00 2001 From: Ryo Nakano Date: Sun, 13 Oct 2024 17:26:39 +0900 Subject: [PATCH 6/6] Fix unsaved changes appear in FilesView when saving another file --- src/Model/DesktopFile.vala | 33 +++++++++++++++++++++++++++++++++ src/View/FilesView.vala | 10 ++++------ 2 files changed, 37 insertions(+), 6 deletions(-) diff --git a/src/Model/DesktopFile.vala b/src/Model/DesktopFile.vala index 7317610..8b63ff3 100644 --- a/src/Model/DesktopFile.vala +++ b/src/Model/DesktopFile.vala @@ -152,6 +152,39 @@ public class Model.DesktopFile : Object { } } + /** + * Value of "Name" entry without unsaved changes. + */ + public string saved_value_name { + owned get { + string? locale = Util.KeyFileUtil.get_locale_for_key (keyfile_clean, KeyFileDesktop.KEY_NAME, Application.preferred_language); + string name = Util.KeyFileUtil.get_locale_string (keyfile_clean, KeyFileDesktop.KEY_NAME, locale); + + return name; + } + } + + /** + * Value of "Icon" entry without unsaved changes. + */ + public string saved_value_icon { + owned get { + return Util.KeyFileUtil.get_string (keyfile_clean, KeyFileDesktop.KEY_ICON); + } + } + + /** + * Value of "Comment" entry without unsaved changes. + */ + public string saved_value_comment { + owned get { + string? locale = Util.KeyFileUtil.get_locale_for_key (keyfile_clean, KeyFileDesktop.KEY_COMMENT, Application.preferred_language); + string comment = Util.KeyFileUtil.get_locale_string (keyfile_clean, KeyFileDesktop.KEY_COMMENT, locale); + + return comment; + } + } + /** * Returns if this contains unsaved changes to the disk. */ diff --git a/src/View/FilesView.vala b/src/View/FilesView.vala index 2c91069..4b4ed68 100644 --- a/src/View/FilesView.vala +++ b/src/View/FilesView.vala @@ -106,15 +106,13 @@ public class View.FilesView : Adw.NavigationPage { private Gtk.Widget create_files_row (Object item) { var file = item as Model.DesktopFile; - string app_name = file.value_name; - var app_icon = new Gtk.Image.from_gicon (new ThemedIcon ("application-x-executable")) { pixel_size = 48, margin_top = 6, margin_bottom = 6 }; try { - app_icon.gicon = Icon.new_for_string (file.value_icon); + app_icon.gicon = Icon.new_for_string (file.saved_value_icon); } catch (Error err) { warning ("Failed to update app_icon: %s", err.message); } @@ -125,7 +123,7 @@ public class View.FilesView : Adw.NavigationPage { }; delete_button.add_css_class ("flat"); delete_button.clicked.connect (() => { - var delete_dialog = setup_delete_dialog (app_name); + var delete_dialog = setup_delete_dialog (file.saved_value_name); delete_dialog.response.connect ((response_id) => { if (response_id != Define.DialogResponse.OK) { @@ -141,8 +139,8 @@ public class View.FilesView : Adw.NavigationPage { }); var row = new Adw.ActionRow () { - title = app_name, - subtitle = file.value_comment, + title = file.saved_value_name, + subtitle = file.saved_value_comment, title_lines = 1, subtitle_lines = 1, activatable = true