Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package otpclient for openSUSE:Factory 
checked in at 2025-12-28 19:19:21
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/otpclient (Old)
 and      /work/SRC/openSUSE:Factory/.otpclient.new.1928 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "otpclient"

Sun Dec 28 19:19:21 2025 rev:41 rq:1324490 version:4.2.0

Changes:
--------
--- /work/SRC/openSUSE:Factory/otpclient/otpclient.changes      2025-05-09 
18:54:02.011756083 +0200
+++ /work/SRC/openSUSE:Factory/.otpclient.new.1928/otpclient.changes    
2025-12-28 19:19:30.335656377 +0100
@@ -1,0 +2,18 @@
+Sat Dec 27 13:32:28 UTC 2025 - Paolo Stivanin <[email protected]>
+
+- Update to 4.2.0:
+  * ADDED: interactive search (ctrl-f)
+  * IMPROVED: search now matches query against type, account label, and issuer 
uniformly
+  * IMPROVED: Streamlined treeview model population to read JSON directly with 
safe defaults
+  * IMPROVED: Simplified OTP update flow and tightened reorder/delete safety 
and cleanup
+  * IMPROVED: Centralized app/db default initialization and early cleanup 
paths in app.c
+  * IMPROVED: Tightened error handling by clearing config migration errors and 
freeing the config path
+  * IMPROVED: Made early-exit cleanup safer by avoiding double-freeing the 
database key
+  * IMPROVED: Added a helper to clear password entries and reset visibility on 
successful submit
+  * IMPROVED: Cleared old/new password fields before the dialog closes to 
avoid brief exposure
+  * IMPROVED: Initialized settings defaults when the config load fails and 
persisted them to otpclient.cfg
+  * IMPROVED: Added warning dialog only when saving fallback defaults fails
+  * IMPROVED: cli: improve robustness and correctness in string and file 
handling
+  * FIXED: duplicate windows and tray icons on re-activation (#409)
+
+-------------------------------------------------------------------

Old:
----
  v4.1.0.tar.gz
  v4.1.0.tar.gz.asc

New:
----
  v4.2.0.tar.gz
  v4.2.0.tar.gz.asc

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ otpclient.spec ++++++
--- /var/tmp/diff_new_pack.92vsuS/_old  2025-12-28 19:19:31.099687746 +0100
+++ /var/tmp/diff_new_pack.92vsuS/_new  2025-12-28 19:19:31.103687911 +0100
@@ -1,7 +1,7 @@
 #
 # spec file for package otpclient
 #
-# Copyright (c) 2025 SUSE LLC
+# Copyright (c) 2025 SUSE LLC and contributors
 #
 # All modifications and additions to the file contributed by third parties
 # remain the property of their copyright owners, unless otherwise agreed
@@ -18,7 +18,7 @@
 
 %define uclname OTPClient
 Name:           otpclient
-Version:        4.1.0
+Version:        4.2.0
 Release:        0
 Summary:        Simple GTK+ client for managing TOTP and HOTP
 License:        GPL-3.0-or-later


++++++ v4.1.0.tar.gz -> v4.2.0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/OTPClient-4.1.0/CMakeLists.txt 
new/OTPClient-4.2.0/CMakeLists.txt
--- old/OTPClient-4.1.0/CMakeLists.txt  2025-05-07 15:52:20.000000000 +0200
+++ new/OTPClient-4.2.0/CMakeLists.txt  2025-12-23 13:40:00.000000000 +0100
@@ -1,5 +1,5 @@
 cmake_minimum_required(VERSION 3.16)
-project(OTPClient VERSION "4.1.0" LANGUAGES "C")
+project(OTPClient VERSION "4.2.0" LANGUAGES "C")
 include(GNUInstallDirs)
 
 configure_file("src/common/version.h.in" "version.h")
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/OTPClient-4.1.0/SECURITY.md 
new/OTPClient-4.2.0/SECURITY.md
--- old/OTPClient-4.1.0/SECURITY.md     2025-05-07 15:52:20.000000000 +0200
+++ new/OTPClient-4.2.0/SECURITY.md     2025-12-23 13:40:00.000000000 +0100
@@ -8,7 +8,7 @@
 |---------|--------------------|-------------|
 |---------|--------------------|-------------|
 | 4.1.x   | :white_check_mark: | -           |
-| 4.0.x   | :white_check_mark: | 30-Jun-2025 |
+| 4.0.x   | :x:                | 30-Jun-2025 |
 | 3.7.x   | :x:                | 30-Sep-2024 |
 | 3.6.x   | :x:                | 31-Aug-2024 |
 | 3.5.x   | :x:                | 31-Mar-2024 |
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/OTPClient-4.1.0/data/com.github.paolostivanin.OTPClient.appdata.xml 
new/OTPClient-4.2.0/data/com.github.paolostivanin.OTPClient.appdata.xml
--- old/OTPClient-4.1.0/data/com.github.paolostivanin.OTPClient.appdata.xml     
2025-05-07 15:52:20.000000000 +0200
+++ new/OTPClient-4.2.0/data/com.github.paolostivanin.OTPClient.appdata.xml     
2025-12-23 13:40:00.000000000 +0100
@@ -89,9 +89,37 @@
   </content_rating>
 
   <releases>
+    <release version="4.2.0" date="2025-12-23">
+      <description>
+        <p>OTPClient 4.2.0 the followin improvements:</p>
+        <ul>
+          <li>ADDED: interactive search (ctrl-f)</li>
+          <li>IMPROVED: search now matches query against type, account label, 
and issuer uniformly</li>
+          <li>IMPROVED: Streamlined treeview model population to read JSON 
directly with safe defaults</li>
+          <li>IMPROVED: Simplified OTP update flow and tightened 
reorder/delete safety and cleanup</li>
+          <li>IMPROVED: Centralized app/db default initialization and early 
cleanup paths in app.c</li>
+          <li>IMPROVED: Tightened error handling by clearing config migration 
errors and freeing the config path</li>
+          <li>IMPROVED: Made early-exit cleanup safer by avoiding 
double-freeing the database key</li>
+          <li>IMPROVED: Added a helper to clear password entries and reset 
visibility on successful submit</li>
+          <li>IMPROVED: Cleared old/new password fields before the dialog 
closes to avoid brief exposure</li>
+          <li>IMPROVED: Initialized settings defaults when the config load 
fails and persisted them to otpclient.cfg</li>
+          <li>IMPROVED: Added warning dialog only when saving fallback 
defaults fails</li>
+          <li>IMPROVED: cli: improve robustness and correctness in string and 
file handling</li>
+          <li>FIXED: duplicate windows and tray icons on re-activation 
(#409)</li>
+        </ul>
+      </description>
+    </release>
+    <release version="4.1.1" date="2025-05-13">
+      <description>
+        <p>OTPClient 4.1.1 the following improvements:</p>
+        <ul>
+          <li>FIXED: build issue on Flatpak</li>
+        </ul>
+      </description>
+    </release>
     <release version="4.1.0" date="2025-05-07">
       <description>
-        <p>OTPClient 4.1.0 the followin improvements:</p>
+        <p>OTPClient 4.1.0 the following improvements:</p>
         <ul>
           <li>ADDED: minimize to tray with ayatana-appindicator3 (#386 thanks 
a lot @len-foss)</li>
           <li>IMPROVED: only show memlock warning dialog when secure memory is 
unavailable (#397)</li>
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/OTPClient-4.1.0/src/cli/exec-action.c 
new/OTPClient-4.2.0/src/cli/exec-action.c
--- old/OTPClient-4.1.0/src/cli/exec-action.c   2025-05-07 15:52:20.000000000 
+0200
+++ new/OTPClient-4.2.0/src/cli/exec-action.c   2025-12-23 13:40:00.000000000 
+0100
@@ -264,8 +264,10 @@
         g_free (db_path);
         return NULL;
     } else {
-        // remove the newline char
-        db_path[g_utf8_strlen (db_path, -1) - 1] = '\0';
+        // Remove the trailing newline (if present). This is UTF-8 safe 
because '\n' is a single-byte ASCII
+        // character and fgets appends it as a separate byte; no multibyte 
code point is modified.
+        char *nl = strchr (db_path, '\n');
+        if (nl) { *nl = '\0'; }
         if (!g_file_test (db_path, G_FILE_TEST_EXISTS)) {
             g_printerr (_("File '%s' does not exist\n"), db_path);
             g_free (cfg_file_path);
@@ -309,9 +311,12 @@
     g_print ("\n");
     tcsetattr (STDIN_FILENO, TCSAFLUSH, &old);
 
-    pwd[g_utf8_strlen (pwd, -1) - 1] = '\0';
+    // Trim trailing newline if present. Safe for UTF-8 because '\n' is a 
single-byte terminator
+    // added by fgets; we do not touch preceding multibyte characters.
+    char *nl = strchr (pwd, '\n');
+    if (nl) { *nl = '\0'; }
 
-    gchar *realloc_pwd = gcry_realloc (pwd, g_utf8_strlen (pwd, -1) + 1);
+    gchar *realloc_pwd = gcry_realloc (pwd, strlen (pwd) + 1);
 
     return realloc_pwd;
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/OTPClient-4.1.0/src/common/common.c 
new/OTPClient-4.2.0/src/common/common.c
--- old/OTPClient-4.1.0/src/common/common.c     2025-05-07 15:52:20.000000000 
+0200
+++ new/OTPClient-4.2.0/src/common/common.c     2025-12-23 13:40:00.000000000 
+0100
@@ -68,9 +68,15 @@
 gchar *
 secure_strdup (const gchar *src)
 {
-    gchar *sec_buf = gcry_calloc_secure (strlen (src) + 1, 1);
-    memcpy (sec_buf, src, strlen (src) + 1);
-
+    if (src == NULL) {
+        return NULL;
+    }
+    size_t len = strlen (src);
+    gchar *sec_buf = gcry_calloc_secure (len + 1, 1);
+    if (sec_buf == NULL) {
+        return NULL;
+    }
+    memcpy (sec_buf, src, len + 1);
     return sec_buf;
 }
 
@@ -78,13 +84,33 @@
 guchar *
 hexstr_to_bytes (const gchar *hexstr)
 {
-    size_t len = g_utf8_strlen (hexstr, -1);
-    size_t final_len = len / 2;
-    guchar *chrs = (guchar *)g_malloc ((final_len+1) * sizeof(*chrs));
-    for (size_t i = 0, j = 0; j < final_len; i += 2, j++)
-        chrs[j] = (hexstr[i] % 32 + 9) % 25 * 16 + (hexstr[i+1] % 32 + 9) % 25;
-    chrs[final_len] = '\0';
-    return chrs;
+    if (hexstr == NULL) {
+        return NULL;
+    }
+    // Hex strings should be ASCII; use strlen, not g_utf8_strlen
+    size_t len = strlen (hexstr);
+    if (len == 0 || (len % 2) != 0) {
+        // invalid length
+        return NULL;
+    }
+
+    size_t out_len = len / 2;
+    guchar *bytes = (guchar *) g_malloc (out_len + 1); // +1 to NUL-terminate 
if needed by callers
+    if (bytes == NULL) {
+        return NULL;
+    }
+
+    for (size_t i = 0, j = 0; j < out_len; i += 2, j++) {
+        int hi = g_ascii_xdigit_value (hexstr[i]);
+        int lo = g_ascii_xdigit_value (hexstr[i + 1]);
+        if (hi < 0 || lo < 0) {
+            g_free (bytes);
+            return NULL;
+        }
+        bytes[j] = (guchar) ((hi << 4) | lo);
+    }
+    bytes[out_len] = '\0';
+    return bytes;
 }
 
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/OTPClient-4.1.0/src/common/file-size.c 
new/OTPClient-4.2.0/src/common/file-size.c
--- old/OTPClient-4.1.0/src/common/file-size.c  2025-05-07 15:52:20.000000000 
+0200
+++ new/OTPClient-4.2.0/src/common/file-size.c  2025-12-23 13:40:00.000000000 
+0100
@@ -1,23 +1,33 @@
 #include <gio/gio.h>
 
+// Returns -1 on error, 0 if file doesn't exist, or the file size on success.
 goffset
 get_file_size (const gchar *file_path)
 {
-    GError *error = NULL;
-
     if (file_path == NULL || !g_file_test (file_path, G_FILE_TEST_EXISTS)) {
         return 0;
     }
 
+    GError *error = NULL;
     GFile *file = g_file_new_for_path (file_path);
-    GFileInfo *info = g_file_query_info (G_FILE(file), "standard::*", 
G_FILE_QUERY_INFO_NONE, NULL, &error);
+    if (file == NULL) {
+        return -1;
+    }
+
+    // Query only the size to avoid unnecessary I/O on other attributes
+    GFileInfo *info = g_file_query_info (file, "standard::size", 
G_FILE_QUERY_INFO_NONE, NULL, &error);
     if (info == NULL) {
-        g_printerr ("%s\n", error->message);
-        g_clear_error (&error);
+        if (error != NULL) {
+            g_printerr ("Failed to query file size: %s\n", error->message);
+            g_clear_error (&error);
+        }
+        g_object_unref (file);
         return -1;
     }
+
     goffset file_size = g_file_info_get_size (info);
 
+    g_object_unref (info);
     g_object_unref (file);
 
     return file_size;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/OTPClient-4.1.0/src/gui/about_diag_cb.c 
new/OTPClient-4.2.0/src/gui/about_diag_cb.c
--- old/OTPClient-4.1.0/src/gui/about_diag_cb.c 2025-05-07 15:52:20.000000000 
+0200
+++ new/OTPClient-4.2.0/src/gui/about_diag_cb.c 2025-12-23 13:40:00.000000000 
+0100
@@ -13,8 +13,6 @@
 
     const gchar *authors[] = {"Paolo Stivanin <[email protected]>", NULL};
     const gchar *artists[] = {"Tobias Bernard (bertob) 
<https://tobiasbernard.com>", NULL};
-    const gchar *partial_path = 
"share/icons/hicolor/scalable/apps/com.github.paolostivanin.OTPClient.svg";
-    gchar *icon_abs_path = g_strconcat (INSTALL_PREFIX, "/", partial_path, 
NULL);
 
     GtkWidget *ab_diag = gtk_about_dialog_new ();
     gtk_window_set_transient_for (GTK_WINDOW(ab_diag), 
GTK_WINDOW(app_data->main_window));
@@ -27,9 +25,7 @@
     gtk_about_dialog_set_website (GTK_ABOUT_DIALOG(ab_diag), 
"https://github.com/paolostivanin/OTPClient";);
     gtk_about_dialog_set_authors (GTK_ABOUT_DIALOG(ab_diag), authors);
     gtk_about_dialog_set_artists (GTK_ABOUT_DIALOG(ab_diag), artists);
-    GdkPixbuf *logo = gdk_pixbuf_new_from_file (icon_abs_path, NULL);
-    gtk_about_dialog_set_logo (GTK_ABOUT_DIALOG(ab_diag), logo);
-    g_free (icon_abs_path);
+    gtk_about_dialog_set_logo_icon_name (GTK_ABOUT_DIALOG(ab_diag), 
"com.github.paolostivanin.OTPClient");
     g_signal_connect (ab_diag, "response", G_CALLBACK (gtk_widget_destroy), 
NULL);
 
     gtk_widget_show_all (ab_diag);
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/OTPClient-4.1.0/src/gui/app.c 
new/OTPClient-4.2.0/src/gui/app.c
--- old/OTPClient-4.1.0/src/gui/app.c   2025-05-07 15:52:20.000000000 +0200
+++ new/OTPClient-4.2.0/src/gui/app.c   2025-12-23 13:40:00.000000000 +0100
@@ -74,42 +74,55 @@
 static void       set_open_db_action        (GtkWidget          *btn,
                                              gpointer            user_data);
 
+static void       init_app_defaults         (AppData            *app_data);
+
+static void       init_db_defaults          (AppData            *app_data);
+
+static void       cleanup_app_data          (AppData            *app_data);
+
+static GQuark app_data_quark = 0;
+
+
+static void
+ensure_app_data_quark (void)
+{
+    if (G_UNLIKELY (app_data_quark == 0))
+        app_data_quark = g_quark_from_static_string ("otpclient-app-data");
+}
+
 
 void
 activate (GtkApplication    *app,
           gpointer           user_data UNUSED)
 {
-    gint32 memlock_value = 0;
-    gint32 memlock_ret_value = set_memlock_value (&memlock_value);
+    ensure_app_data_quark ();
 
-    AppData *app_data = g_new0 (AppData, 1);
+    AppData *app_data = g_object_get_qdata (G_OBJECT (app), app_data_quark);
+    if (app_data != NULL) {
+        gtk_window_present (GTK_WINDOW (app_data->main_window));
+        return;
+    }
 
-    app_data->app_locked = FALSE;
+    gint32 memlock_value = 0;
+    gint32 memlock_ret_value = set_memlock_value (&memlock_value);
 
-    gint width = 0, height = 0;
-    app_data->show_next_otp = FALSE; // next otp not shown by default
-    app_data->disable_notifications = FALSE; // notifications enabled by 
default
-    app_data->search_column = 0; // account
-    app_data->auto_lock = FALSE; // disabled by default
-    app_data->inactivity_timeout = 0; // never
-    app_data->use_dark_theme = FALSE; // light theme by default
-    app_data->use_secret_service = TRUE; // secret service enabled by default
-    app_data->is_reorder_active = FALSE; // when app is started, reorder is 
not set
-    // open_db_file_action is set only on first startup and not when the db is 
deleted but the cfg file is there, therefore we need a default action
-    app_data->open_db_file_action = GTK_FILE_CHOOSER_ACTION_SAVE;
+    app_data = g_new0 (AppData, 1);
+    init_app_defaults (app_data);
     app_data->builder = get_builder_from_partial_path (UI_PARTIAL_PATH);
     app_data->add_popover_builder = get_builder_from_partial_path 
(AP_PARTIAL_PATH);
     app_data->settings_popover_builder = get_builder_from_partial_path 
(SP_PARTIAL_PATH);
 
-    set_config_data (&width, &height, app_data);
-
     app_data->db_data = g_new0 (DatabaseData, 1);
-    app_data->db_data->key_stored = FALSE; // at startup, we don't know 
whether the key is stored or not
+    init_db_defaults (app_data);
+
+    gint width = 0;
+    gint height = 0;
+    set_config_data (&width, &height, app_data);
 
     create_main_window (width, height, app_data);
     if (app_data->main_window == NULL) {
         g_printerr ("%s\n", _("Couldn't locate the ui file, exiting..."));
-        g_free (app_data->db_data);
+        cleanup_app_data (app_data);
         g_application_quit (G_APPLICATION(app));
         return;
     }
@@ -121,7 +134,7 @@
                                         "<a 
href=\"https://github.com/paolostivanin/OTPClient/wiki/Secure-Memory-Limitations\";>secure
 memory</a> wiki page before re-running OTPClient."));
         show_message_dialog (app_data->main_window, msg, GTK_MESSAGE_ERROR);
         g_free (msg);
-        g_free (app_data->db_data);
+        cleanup_app_data (app_data);
         g_application_quit (G_APPLICATION(app));
         return;
     }
@@ -130,7 +143,7 @@
     if (init_msg != NULL) {
         show_message_dialog (app_data->main_window, init_msg, 
GTK_MESSAGE_ERROR);
         g_free (init_msg);
-        g_free (app_data->db_data);
+        cleanup_app_data (app_data);
         g_application_quit (G_APPLICATION(app));
         return;
     }
@@ -144,9 +157,9 @@
             app_data->db_data->db_path = db_path;
         } else {
             // Use the default path only if no path is set in config
-            app_data->db_data->db_path = g_build_filenam e(g_get_user_data_dir 
(), "otpclient-db.enc", NULL);
+            app_data->db_data->db_path = g_build_filename(g_get_user_data_dir 
(), "otpclient-db.enc", NULL);
             gchar *cfg_file_path = g_build_filename (g_get_user_data_dir (), 
"otpclient.cfg", NULL);
-            if (g_file_tes t(cfg_file_path, G_FILE_TEST_EXISTS)) {
+            if (g_file_test(cfg_file_path, G_FILE_TEST_EXISTS)) {
                 g_key_file_set_string (kf, "config", "db_path", 
app_data->db_data->db_path);
                 g_key_file_save_to_file (kf, cfg_file_path, NULL);
             }
@@ -179,8 +192,7 @@
             case GTK_RESPONSE_CANCEL:
             default:
                 gtk_widget_destroy (app_data->diag_rcdb);
-                g_free (app_data->db_data);
-                g_free (app_data);
+                cleanup_app_data (app_data);
                 g_application_quit (G_APPLICATION(app));
                 return;
             case GTK_RESPONSE_OK:
@@ -190,8 +202,7 @@
 
     app_data->db_data->db_path = get_db_path (app_data);
     if (app_data->db_data->db_path == NULL) {
-        g_free (app_data->db_data);
-        g_free (app_data);
+        cleanup_app_data (app_data);
         g_application_quit (G_APPLICATION(app));
         return;
     }
@@ -218,8 +229,7 @@
         if (app_data->db_data->key == NULL) {
             retry_change_file:
             if (change_file (app_data) == QUIT_APP) {
-                g_free (app_data->db_data);
-                g_free (app_data);
+                cleanup_app_data (app_data);
                 g_application_quit (G_APPLICATION(app));
                 return;
             }
@@ -236,8 +246,7 @@
         ), memlock_value);
         g_printerr ("%s\n", msg);
         g_free (msg);
-        g_free (app_data->db_data);
-        g_free (app_data);
+        cleanup_app_data (app_data);
         g_application_quit (G_APPLICATION(app));
         return;
     }
@@ -246,10 +255,9 @@
     load_db (app_data->db_data, &err);
     if (err != NULL && !g_error_matches (err, missing_file_gquark (), 
MISSING_FILE_ERRCODE)) {
         show_message_dialog (app_data->main_window, err->message, 
GTK_MESSAGE_ERROR);
-        gcry_free (app_data->db_data->key);
+        g_clear_pointer (&app_data->db_data->key, gcry_free);
         if (g_error_matches (err, memlock_error_gquark (), MEMLOCK_ERRCODE)) {
-            g_free (app_data->db_data);
-            g_free (app_data);
+            cleanup_app_data (app_data);
             g_clear_error (&err);
             g_application_quit (G_APPLICATION(app));
             return;
@@ -289,10 +297,7 @@
     GtkToggleButton *reorder_toggle_btn = 
GTK_TOGGLE_BUTTON(gtk_builder_get_object (app_data->builder, 
"reorder_toggle_btn_id"));
     g_signal_connect (app_data->main_window, "toggle-reorder-button", 
G_CALLBACK(toggle_button_cb), reorder_toggle_btn);
     g_signal_connect (reorder_toggle_btn, "toggled", 
G_CALLBACK(reorder_rows_cb), app_data);
-    g_signal_connect (app_data->main_window, "key_press_event", 
G_CALLBACK(key_pressed_cb), NULL);
-
-    g_signal_connect (app_data->main_window, "key_press_event", 
G_CALLBACK(key_pressed_cb), NULL);
-
+    g_signal_connect (app_data->main_window, "key_press_event", 
G_CALLBACK(key_pressed_cb), app_data);
     g_signal_connect (app_data->main_window, "destroy", 
G_CALLBACK(destroy_cb), app_data);
 
     app_data->source_id = g_timeout_add_full (G_PRIORITY_DEFAULT, 1000, 
traverse_liststore, app_data, NULL);
@@ -303,21 +308,43 @@
     app_data->last_user_activity = g_date_time_new_now_local ();
     app_data->source_id_last_activity = g_timeout_add_seconds (1, 
check_inactivity, app_data);
 
+    g_object_set_qdata (G_OBJECT (app), app_data_quark, app_data);
+
     gtk_widget_show_all (app_data->main_window);
+    gtk_widget_hide(app_data->search_entry);
 }
 
 
 static gboolean
 key_pressed_cb (GtkWidget   *window,
                 GdkEventKey *event_key,
-                gpointer     user_data UNUSED)
+                gpointer     user_data)
 {
+    CAST_USER_DATA(AppData, app_data, user_data);
     switch (event_key->keyval) {
         case GDK_KEY_q:
         if (event_key->state & GDK_CONTROL_MASK) {
             gtk_window_close (GTK_WINDOW(window));
         }
         break;
+        case GDK_KEY_f:
+            if (event_key->state & GDK_CONTROL_MASK) {
+                GtkWidget *search_entry = app_data->search_entry;
+                if (search_entry != NULL) {
+                    gboolean is_visible = gtk_widget_get_visible 
(search_entry);
+                    gtk_widget_set_visible (search_entry, !is_visible);
+                    if (!is_visible) {
+                        gtk_widget_grab_focus (search_entry);
+                    } else {
+                        gtk_entry_set_text (GTK_ENTRY(search_entry), "");
+                        if (app_data->tree_view != NULL) {
+                            gtk_widget_grab_focus 
(GTK_WIDGET(app_data->tree_view));
+                        }
+                    }
+                }
+                return TRUE;
+            }
+            break;
     }
     return FALSE;
 }
@@ -336,7 +363,6 @@
         *height = g_key_file_get_integer (kf, "config", "window_height", NULL);
         app_data->show_next_otp = g_key_file_get_boolean (kf, "config", 
"show_next_otp", NULL);
         app_data->disable_notifications = g_key_file_get_boolean (kf, 
"config", "notifications", NULL);
-        app_data->search_column = g_key_file_get_integer (kf, "config", 
"search_column", NULL);
         app_data->auto_lock = g_key_file_get_boolean (kf, "config", 
"auto_lock", NULL);
         app_data->inactivity_timeout = g_key_file_get_integer (kf, "config", 
"inactivity_timeout", NULL);
         app_data->use_dark_theme = g_key_file_get_boolean (kf, "config", 
"dark_theme", NULL);
@@ -351,6 +377,7 @@
             // key was not found, so we already migrated to the new format
             app_data->use_secret_service = g_key_file_get_boolean (kf, 
"config", "use_secret_service", NULL);
         }
+        g_clear_error (&err);
         // end migration
         g_object_set (gtk_settings_get_default (), 
"gtk-application-prefer-dark-theme", app_data->use_dark_theme, NULL);
         g_key_file_free (kf);
@@ -379,6 +406,7 @@
         g_free (err_msg);
         g_clear_error (&err);
     }
+    g_free (cfg_file_path);
 }
 
 static void
@@ -580,6 +608,11 @@
             gpointer     user_data)
 {
     CAST_USER_DATA(AppData, app_data, user_data);
+
+    ensure_app_data_quark ();
+    GtkApplication *app = GTK_APPLICATION(gtk_window_get_application 
(GTK_WINDOW (window)));
+    g_object_set_qdata (G_OBJECT(app), app_data_quark, NULL);
+
     save_sort_order (app_data->tree_view);
     g_source_remove (app_data->source_id);
     g_source_remove (app_data->source_id_last_activity);
@@ -605,6 +638,12 @@
     g_object_unref (app_data->builder);
     g_object_unref (app_data->add_popover_builder);
     g_object_unref (app_data->settings_popover_builder);
+    if (app_data->filter_model != NULL) {
+        g_object_unref (app_data->filter_model);
+    }
+    if (app_data->list_store != NULL) {
+        g_object_unref (app_data->list_store);
+    }
     g_free (app_data);
     gcry_control (GCRYCTL_TERM_SECMEM);
 }
@@ -615,7 +654,11 @@
 {
     gint id;
     GtkSortType order;
-    gtk_tree_sortable_get_sort_column_id 
(GTK_TREE_SORTABLE(GTK_LIST_STORE(gtk_tree_view_get_model (tree_view))), &id, 
&order);
+    GtkTreeModel *model = gtk_tree_view_get_model (tree_view);
+    if (GTK_IS_TREE_MODEL_FILTER (model)) {
+        model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(model));
+    }
+    gtk_tree_sortable_get_sort_column_id (GTK_TREE_SORTABLE(model), &id, 
&order);
     // store data only if it was changed
     if (id >= 0) {
         store_data ("column_id", id, "sort_order", order);
@@ -671,3 +714,58 @@
     app_data->open_db_file_action = g_strcmp0 (gtk_widget_get_name (btn), 
"diag_rc_restoredb_btn") == 0 ? GTK_FILE_CHOOSER_ACTION_OPEN : 
GTK_FILE_CHOOSER_ACTION_SAVE;
     gtk_dialog_response (GTK_DIALOG(app_data->diag_rcdb), GTK_RESPONSE_OK);
 }
+
+static void
+init_app_defaults (AppData *app_data)
+{
+    app_data->app_locked = FALSE;
+    app_data->show_next_otp = FALSE; // next otp not shown by default
+    app_data->disable_notifications = FALSE; // notifications enabled by 
default
+    app_data->auto_lock = FALSE; // disabled by default
+    app_data->inactivity_timeout = 0; // never
+    app_data->use_dark_theme = FALSE; // light theme by default
+    app_data->use_secret_service = TRUE; // secret service enabled by default
+    app_data->is_reorder_active = FALSE; // when app is started, reorder is 
not set
+    app_data->use_tray = FALSE; // do not use tray by default
+    // open_db_file_action is set only on first startup and not when the db is 
deleted but the cfg file is there, therefore we need a default action
+    app_data->open_db_file_action = GTK_FILE_CHOOSER_ACTION_SAVE;
+}
+
+static void
+init_db_defaults (AppData *app_data)
+{
+    app_data->db_data->key_stored = FALSE; // at startup, we don't know 
whether the key is stored or not
+    app_data->db_data->max_file_size_from_memlock = 0;
+    app_data->db_data->objects_hash = NULL;
+    app_data->db_data->data_to_add = NULL;
+}
+
+static void
+cleanup_app_data (AppData *app_data)
+{
+    if (app_data == NULL) {
+        return;
+    }
+
+    if (app_data->main_window != NULL) {
+        gtk_widget_destroy (app_data->main_window);
+    }
+
+    if (app_data->builder != NULL) {
+        g_object_unref (app_data->builder);
+    }
+    if (app_data->add_popover_builder != NULL) {
+        g_object_unref (app_data->add_popover_builder);
+    }
+    if (app_data->settings_popover_builder != NULL) {
+        g_object_unref (app_data->settings_popover_builder);
+    }
+
+    if (app_data->db_data != NULL) {
+        g_clear_pointer (&app_data->db_data->db_path, g_free);
+        g_clear_pointer (&app_data->db_data->key, gcry_free);
+        g_free (app_data->db_data);
+    }
+
+    g_free (app_data);
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/OTPClient-4.1.0/src/gui/change-db-cb.c 
new/OTPClient-4.2.0/src/gui/change-db-cb.c
--- old/OTPClient-4.1.0/src/gui/change-db-cb.c  2025-05-07 15:52:20.000000000 
+0200
+++ new/OTPClient-4.2.0/src/gui/change-db-cb.c  2025-12-23 13:40:00.000000000 
+0100
@@ -65,7 +65,7 @@
             gtk_widget_hide (changedb_diag);
             return QUIT_APP;
     }
-    gtk_widget_destroy (changedb_diag);
+    gtk_widget_hide (changedb_diag);
 
     return CHANGE_OK;
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/OTPClient-4.1.0/src/gui/data.h 
new/OTPClient-4.2.0/src/gui/data.h
--- old/OTPClient-4.1.0/src/gui/data.h  2025-05-07 15:52:20.000000000 +0200
+++ new/OTPClient-4.2.0/src/gui/data.h  2025-12-23 13:40:00.000000000 +0100
@@ -18,6 +18,9 @@
 
     GtkWidget *main_window;
     GtkTreeView *tree_view;
+    GtkListStore *list_store;
+    GtkTreeModelFilter *filter_model;
+    GtkWidget *search_entry;
     #ifdef ENABLE_MINIMIZE_TO_TRAY
     AppIndicator *indicator;
     #endif
@@ -26,7 +29,6 @@
 
     gboolean show_next_otp;
     gboolean disable_notifications;
-    gint search_column;
     gboolean auto_lock;
     gint inactivity_timeout;
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/OTPClient-4.1.0/src/gui/edit-row-cb.c 
new/OTPClient-4.2.0/src/gui/edit-row-cb.c
--- old/OTPClient-4.1.0/src/gui/edit-row-cb.c   2025-05-07 15:52:20.000000000 
+0200
+++ new/OTPClient-4.2.0/src/gui/edit-row-cb.c   2025-12-23 13:40:00.000000000 
+0100
@@ -44,12 +44,8 @@
     CAST_USER_DATA(AppData, app_data, user_data);
     edit_data->db_data = app_data->db_data;
 
-    GtkTreeModel *model = gtk_tree_view_get_model (app_data->tree_view);
-
-    edit_data->list_store = GTK_LIST_STORE(model);
-
-    if (gtk_tree_selection_get_selected (gtk_tree_view_get_selection 
(app_data->tree_view), &model, &edit_data->iter)) {
-        gtk_tree_model_get (model, &edit_data->iter, COLUMN_ACC_LABEL, 
&edit_data->current_label, COLUMN_ACC_ISSUER, &edit_data->current_issuer, -1);
+    if (get_selected_liststore_iter (app_data, &edit_data->list_store, 
&edit_data->iter)) {
+        gtk_tree_model_get (GTK_TREE_MODEL(edit_data->list_store), 
&edit_data->iter, COLUMN_ACC_LABEL, &edit_data->current_label, 
COLUMN_ACC_ISSUER, &edit_data->current_issuer, -1);
         show_edit_dialog (edit_data, app_data);
         g_free (edit_data->current_label);
         g_free (edit_data->current_issuer);
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/OTPClient-4.1.0/src/gui/gui-misc.c 
new/OTPClient-4.2.0/src/gui/gui-misc.c
--- old/OTPClient-4.1.0/src/gui/gui-misc.c      2025-05-07 15:52:20.000000000 
+0200
+++ new/OTPClient-4.2.0/src/gui/gui-misc.c      2025-12-23 13:40:00.000000000 
+0100
@@ -303,4 +303,30 @@
     update_model (app_data);
     g_slist_free_full (app_data->db_data->data_to_add, json_free);
     app_data->db_data->data_to_add = NULL;
+}
+
+
+gboolean
+get_selected_liststore_iter (AppData       *app_data,
+                             GtkListStore **list_store,
+                             GtkTreeIter   *iter)
+{
+    GtkTreeModel *model = gtk_tree_view_get_model (app_data->tree_view);
+    GtkTreeIter view_iter;
+    if (!gtk_tree_selection_get_selected (gtk_tree_view_get_selection 
(app_data->tree_view), &model, &view_iter)) {
+        return FALSE;
+    }
+
+    if (GTK_IS_TREE_MODEL_FILTER (model)) {
+        GtkTreeIter child_iter;
+        GtkTreeModel *child_model = gtk_tree_model_filter_get_model 
(GTK_TREE_MODEL_FILTER(model));
+        gtk_tree_model_filter_convert_iter_to_child_iter 
(GTK_TREE_MODEL_FILTER(model), &child_iter, &view_iter);
+        *list_store = GTK_LIST_STORE(child_model);
+        *iter = child_iter;
+        return TRUE;
+    }
+
+    *list_store = GTK_LIST_STORE(model);
+    *iter = view_iter;
+    return TRUE;
 }
\ No newline at end of file
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/OTPClient-4.1.0/src/gui/gui-misc.h 
new/OTPClient-4.2.0/src/gui/gui-misc.h
--- old/OTPClient-4.1.0/src/gui/gui-misc.h      2025-05-07 15:52:20.000000000 
+0200
+++ new/OTPClient-4.2.0/src/gui/gui-misc.h      2025-12-23 13:40:00.000000000 
+0100
@@ -14,6 +14,10 @@
 guint     get_row_number_from_iter     (GtkListStore   *list_store,
                                         GtkTreeIter     iter);
 
+gboolean  get_selected_liststore_iter  (AppData        *app_data,
+                                        GtkListStore  **list_store,
+                                        GtkTreeIter    *iter);
+
 void      send_ok_cb                   (GtkWidget      *entry,
                                         gpointer        user_data);
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/OTPClient-4.1.0/src/gui/liststore-misc.c 
new/OTPClient-4.2.0/src/gui/liststore-misc.c
--- old/OTPClient-4.1.0/src/gui/liststore-misc.c        2025-05-07 
15:52:20.000000000 +0200
+++ new/OTPClient-4.2.0/src/gui/liststore-misc.c        2025-12-23 
13:40:00.000000000 +0100
@@ -33,7 +33,9 @@
 traverse_liststore (gpointer user_data)
 {
     CAST_USER_DATA(AppData, app_data, user_data);
-    gtk_tree_model_foreach (GTK_TREE_MODEL(gtk_tree_view_get_model 
(app_data->tree_view)), foreach_func_update_otps, app_data);
+    if (app_data->list_store != NULL) {
+        gtk_tree_model_foreach (GTK_TREE_MODEL(app_data->list_store), 
foreach_func_update_otps, app_data);
+    }
 
     return TRUE;
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/OTPClient-4.1.0/src/gui/password-cb.c 
new/OTPClient-4.2.0/src/gui/password-cb.c
--- old/OTPClient-4.1.0/src/gui/password-cb.c   2025-05-07 15:52:20.000000000 
+0200
+++ new/OTPClient-4.2.0/src/gui/password-cb.c   2025-12-23 13:40:00.000000000 
+0100
@@ -122,6 +122,17 @@
 
 
 static void
+reset_entry_after_submit (GtkWidget *entry)
+{
+    if (entry == NULL) {
+        return;
+    }
+    gtk_entry_set_text (GTK_ENTRY(entry), "");
+    gtk_entry_set_visibility (GTK_ENTRY(entry), FALSE);
+}
+
+
+static void
 check_pwd_cb (GtkWidget   *entry,
               gpointer     user_data)
 {
@@ -137,6 +148,8 @@
         return;
     }
     if (g_strcmp0 (gtk_entry_get_text (GTK_ENTRY(entry_widgets->entry1)), 
gtk_entry_get_text (GTK_ENTRY(entry_widgets->entry2))) == 0) {
+        reset_entry_after_submit (entry_widgets->entry_old);
+        reset_entry_after_submit (entry_widgets->entry2);
         password_cb (entry, (gpointer *)&entry_widgets->pwd);
         entry_widgets->retry = FALSE;
     } else {
@@ -154,6 +167,7 @@
     gsize len = g_utf8_strlen (text, -1) + 1;
     *pwd = gcry_calloc_secure (len, 1);
     strncpy (*pwd, text, len);
+    reset_entry_after_submit (entry);
     GtkWidget *top_level = gtk_widget_get_toplevel (entry);
     gtk_dialog_response (GTK_DIALOG (top_level), GTK_RESPONSE_CLOSE);
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/OTPClient-4.1.0/src/gui/settings-cb.c 
new/OTPClient-4.2.0/src/gui/settings-cb.c
--- old/OTPClient-4.1.0/src/gui/settings-cb.c   2025-05-07 15:52:20.000000000 
+0200
+++ new/OTPClient-4.2.0/src/gui/settings-cb.c   2025-12-23 13:40:00.000000000 
+0100
@@ -57,37 +57,42 @@
     GError *err = NULL;
     GKeyFile *kf = g_key_file_new ();
     if (!g_key_file_load_from_file (kf, cfg_file_path, G_KEY_FILE_NONE, &err)) 
{
-        gchar *msg = g_strconcat ("Couldn't get data from config file: ", 
err->message, NULL);
-        show_message_dialog (app_data->main_window, msg, GTK_MESSAGE_ERROR);
-        g_free (msg);
-        g_free (cfg_file_path);
-        g_key_file_free (kf);
-        g_clear_error (&err);
-        g_free (settings_data);
-        return;
-    }
-
-    // if key is not found, g_key_file_get_boolean returns FALSE and 
g_key_file_get_integer returns 0.
-    // Therefore, having these values as default is exactly what we want. So 
no need to check whether the key is missing.
-    app_data->show_next_otp = g_key_file_get_boolean (kf, "config", 
"show_next_otp", NULL);
-    app_data->disable_notifications = g_key_file_get_boolean (kf, "config", 
"notifications", NULL);
-    app_data->search_column = g_key_file_get_integer (kf, "config", 
"search_column", NULL);
-    app_data->auto_lock = g_key_file_get_boolean (kf, "config", "auto_lock", 
NULL);
-    app_data->inactivity_timeout = g_key_file_get_integer (kf, "config", 
"inactivity_timeout", NULL);
-    app_data->use_dark_theme = g_key_file_get_boolean (kf, "config", 
"dark_theme", NULL);
-    app_data->use_secret_service = g_key_file_get_boolean (kf, "config", 
"use_secret_service", &err);
-    app_data->use_tray = g_key_file_get_boolean (kf, "config", "use_tray", 
NULL);
-    if (err != NULL && g_error_matches (err, G_KEY_FILE_ERROR, 
G_KEY_FILE_ERROR_KEY_NOT_FOUND)) {
-        // if the key is not found, we set it to TRUE and save it to the 
config file.
-        app_data->use_secret_service = TRUE;
+        // if config file is not set, we use the default values.
         g_clear_error (&err);
+        g_key_file_set_boolean (kf, "config", "show_next_otp", 
app_data->show_next_otp);
+        g_key_file_set_boolean (kf, "config", "notifications", 
app_data->disable_notifications);
+        g_key_file_set_boolean (kf, "config", "auto_lock", 
app_data->auto_lock);
+        g_key_file_set_integer (kf, "config", "inactivity_timeout", 
app_data->inactivity_timeout);
+        g_key_file_set_boolean (kf, "config", "dark_theme", 
app_data->use_dark_theme);
+        g_key_file_set_boolean (kf, "config", "use_secret_service", 
app_data->use_secret_service);
+        g_key_file_set_boolean (kf, "config", "use_tray", app_data->use_tray);
+        if (!g_key_file_save_to_file (kf, cfg_file_path, &err)) {
+            gchar *msg = g_strconcat (_("Couldn't save default settings: "), 
err->message, NULL);
+            show_message_dialog (app_data->main_window, msg, 
GTK_MESSAGE_WARNING);
+            g_free (msg);
+            g_clear_error (&err);
+        }
+    } else {
+        // if key is not found, g_key_file_get_boolean returns FALSE and 
g_key_file_get_integer returns 0.
+        // Therefore, having these values as default is exactly what we want. 
So no need to check whether the key is missing.
+        app_data->show_next_otp = g_key_file_get_boolean (kf, "config", 
"show_next_otp", NULL);
+        app_data->disable_notifications = g_key_file_get_boolean (kf, 
"config", "notifications", NULL);
+        app_data->auto_lock = g_key_file_get_boolean (kf, "config", 
"auto_lock", NULL);
+        app_data->inactivity_timeout = g_key_file_get_integer (kf, "config", 
"inactivity_timeout", NULL);
+        app_data->use_dark_theme = g_key_file_get_boolean (kf, "config", 
"dark_theme", NULL);
+        app_data->use_secret_service = g_key_file_get_boolean (kf, "config", 
"use_secret_service", &err);
+        app_data->use_tray = g_key_file_get_boolean (kf, "config", "use_tray", 
NULL);
+        if (err != NULL && g_error_matches (err, G_KEY_FILE_ERROR, 
G_KEY_FILE_ERROR_KEY_NOT_FOUND)) {
+            // if the key is not found, we set it to TRUE and save it to the 
config file.
+            app_data->use_secret_service = TRUE;
+            g_clear_error (&err);
+        }
     }
 
     GtkBuilder *builder = get_builder_from_partial_path(UI_PARTIAL_PATH);
     GtkWidget *dialog = GTK_WIDGET(gtk_builder_get_object (builder, 
"settings_diag_id"));
     GtkWidget *sno_switch = GTK_WIDGET(gtk_builder_get_object (builder, 
"nextotp_switch_id"));
     GtkWidget *dn_switch = GTK_WIDGET(gtk_builder_get_object (builder, 
"notif_switch_id"));
-    GtkWidget *sc_cb = GTK_WIDGET(gtk_builder_get_object (builder, 
"search_by_cb_id"));
     settings_data->al_switch = GTK_WIDGET(gtk_builder_get_object (builder, 
"autolock_switch_id"));
     g_signal_connect (settings_data->al_switch, "state-set", 
G_CALLBACK(handle_secretservice_switch), settings_data);
     settings_data->inactivity_cb = GTK_WIDGET(gtk_builder_get_object (builder, 
"autolock_inactive_cb_id"));
@@ -111,10 +116,7 @@
     gtk_switch_set_active (GTK_SWITCH(dt_switch), app_data->use_dark_theme);
     gtk_switch_set_active (GTK_SWITCH(settings_data->dss_switch), 
app_data->use_secret_service);
     gtk_switch_set_active (GTK_SWITCH(settings_data->tray_switch), 
app_data->use_tray);
-    gchar *active_id_string = g_strdup_printf ("%d", app_data->search_column);
-    gtk_combo_box_set_active_id (GTK_COMBO_BOX(sc_cb), active_id_string);
-    g_free (active_id_string);
-    active_id_string = g_strdup_printf ("%d", app_data->inactivity_timeout);
+    gchar *active_id_string = g_strdup_printf ("%d", 
app_data->inactivity_timeout);
     gtk_combo_box_set_active_id (GTK_COMBO_BOX(settings_data->inactivity_cb), 
active_id_string);
     g_free (active_id_string);
 
@@ -127,7 +129,6 @@
         case GTK_RESPONSE_OK:
             app_data->show_next_otp = gtk_switch_get_active 
(GTK_SWITCH(sno_switch));
             app_data->disable_notifications = gtk_switch_get_active 
(GTK_SWITCH(dn_switch));
-            app_data->search_column = (gint)g_ascii_strtoll 
(gtk_combo_box_get_active_id (GTK_COMBO_BOX(sc_cb)), NULL, 10);
             app_data->auto_lock = gtk_switch_get_active 
(GTK_SWITCH(settings_data->al_switch));
             app_data->inactivity_timeout = (gint)g_ascii_strtoll 
(gtk_combo_box_get_active_id (GTK_COMBO_BOX(settings_data->inactivity_cb)), 
NULL, 10);
             app_data->use_dark_theme = gtk_switch_get_active 
(GTK_SWITCH(dt_switch));
@@ -135,7 +136,6 @@
             app_data->use_tray = gtk_switch_get_active 
(GTK_SWITCH(settings_data->tray_switch));
             g_key_file_set_boolean (kf, "config", "show_next_otp", 
app_data->show_next_otp);
             g_key_file_set_boolean (kf, "config", "notifications", 
app_data->disable_notifications);
-            g_key_file_set_integer (kf, "config", "search_column", 
app_data->search_column);
             g_key_file_set_boolean (kf, "config", "auto_lock", 
app_data->auto_lock);
             g_key_file_set_integer (kf, "config", "inactivity_timeout", 
app_data->inactivity_timeout);
             g_key_file_set_boolean (kf, "config", "dark_theme", 
app_data->use_dark_theme);
@@ -148,7 +148,9 @@
             if (!g_key_file_save_to_file (kf, cfg_file_path, NULL)) {
                 g_printerr ("%s\n", _("Error while saving the config file."));
             }
-            gtk_tree_view_set_search_column 
(GTK_TREE_VIEW(app_data->tree_view), app_data->search_column + 1);
+            if (app_data->filter_model != NULL) {
+                gtk_tree_model_filter_refilter (app_data->filter_model);
+            }
             break;
         case GTK_RESPONSE_CANCEL:
             break;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/OTPClient-4.1.0/src/gui/show-qr-cb.c 
new/OTPClient-4.2.0/src/gui/show-qr-cb.c
--- old/OTPClient-4.1.0/src/gui/show-qr-cb.c    2025-05-07 15:52:20.000000000 
+0200
+++ new/OTPClient-4.2.0/src/gui/show-qr-cb.c    2025-12-23 13:40:00.000000000 
+0100
@@ -25,11 +25,10 @@
 {
     CAST_USER_DATA(AppData, app_data, user_data);
 
-    GtkTreeModel *model = gtk_tree_view_get_model (app_data->tree_view);
-    GtkListStore *list_store = GTK_LIST_STORE(model);
+    GtkListStore *list_store = NULL;
     GtkTreeIter iter;
 
-    if (gtk_tree_selection_get_selected (gtk_tree_view_get_selection 
(app_data->tree_view), &model, &iter) == FALSE) {
+    if (!get_selected_liststore_iter (app_data, &list_store, &iter)) {
         show_message_dialog (app_data->main_window, "Error: a row must be 
selected in order to get the QR Code.", GTK_MESSAGE_ERROR);
         return;
     }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/OTPClient-4.1.0/src/gui/treeview.c 
new/OTPClient-4.2.0/src/gui/treeview.c
--- old/OTPClient-4.1.0/src/gui/treeview.c      2025-05-07 15:52:20.000000000 
+0200
+++ new/OTPClient-4.2.0/src/gui/treeview.c      2025-12-23 13:40:00.000000000 
+0100
@@ -6,17 +6,11 @@
 #include "message-dialogs.h"
 #include "edit-row-cb.h"
 #include "show-qr-cb.h"
+#include "gui-misc.h"
 
 
-typedef struct parsed_json_data_t {
-    gchar **types;
-    gchar **labels;
-    gchar **issuers;
-    GArray *periods;
-} ParsedData;
-
-static void     set_json_data      (json_t         *array,
-                                    ParsedData     *pjd);
+static const gchar *json_get_string_or_empty (json_t      *obj,
+                                              const gchar *key);
 
 static void     add_data_to_model  (DatabaseData   *db_data,
                                     GtkListStore   *store);
@@ -33,9 +27,29 @@
                                     GtkTreeIter    *iter,
                                     gpointer        user_data);
 
-static void     free_pjd           (ParsedData     *pjd);
+static gboolean on_treeview_button_press_event (GtkWidget *treeview,
+                                                GdkEventButton *event,
+                                                gpointer user_data);
+
+static gboolean filter_visible_func (GtkTreeModel *model,
+                                     GtkTreeIter  *iter,
+                                     gpointer      user_data);
+
+static gboolean row_matches_query (GtkTreeModel *model,
+                                   GtkTreeIter  *iter,
+                                   const gchar  *query_folded);
+
+static void     search_entry_changed_cb (GtkEntry *entry,
+                                         gpointer  user_data);
+
+static void     search_entry_activate_cb (GtkEntry *entry,
+                                          gpointer  user_data);
+
+static void     select_first_row (AppData *app_data);
 
-static gboolean on_treeview_button_press_event (GtkWidget *treeview, 
GdkEventButton *event, gpointer user_data);
+static gboolean get_liststore_iter_from_path (AppData     *app_data,
+                                              GtkTreePath *path,
+                                              GtkTreeIter *iter);
 
 
 void
@@ -43,17 +57,24 @@
 {
     app_data->tree_view = GTK_TREE_VIEW(gtk_builder_get_object 
(app_data->builder, "treeview_id"));
 
-    GtkListStore *list_store = gtk_list_store_new (NUM_COLUMNS, G_TYPE_STRING, 
G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING,
-                                                   G_TYPE_UINT, G_TYPE_UINT, 
G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, G_TYPE_INT);
+    app_data->list_store = gtk_list_store_new (NUM_COLUMNS, G_TYPE_STRING, 
G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING,
+                                               G_TYPE_UINT, G_TYPE_UINT, 
G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, G_TYPE_INT);
 
     add_columns (app_data->tree_view);
 
-    add_data_to_model (app_data->db_data, list_store);
+    add_data_to_model (app_data->db_data, app_data->list_store);
 
-    gtk_tree_view_set_model (app_data->tree_view, GTK_TREE_MODEL(list_store));
+    app_data->filter_model = GTK_TREE_MODEL_FILTER(gtk_tree_model_filter_new 
(GTK_TREE_MODEL(app_data->list_store), NULL));
+    gtk_tree_model_filter_set_visible_func (app_data->filter_model, 
filter_visible_func, app_data, NULL);
 
-    // model has id 0 for type, 1 for label, 2 for issuer, etc while ui file 
has 0 label and 1 issuer. That's why the  "+1"
-    gtk_tree_view_set_search_column (GTK_TREE_VIEW(app_data->tree_view), 
app_data->search_column + 1);
+    gtk_tree_view_set_model (app_data->tree_view, 
GTK_TREE_MODEL(app_data->filter_model));
+
+    app_data->search_entry = GTK_WIDGET(gtk_builder_get_object 
(app_data->builder, "search_entry_id"));
+    if (app_data->search_entry != NULL) {
+        gtk_tree_view_set_search_entry (GTK_TREE_VIEW(app_data->tree_view), 
GTK_ENTRY(app_data->search_entry));
+        g_signal_connect (app_data->search_entry, "changed", 
G_CALLBACK(search_entry_changed_cb), app_data);
+        g_signal_connect (app_data->search_entry, "activate", 
G_CALLBACK(search_entry_activate_cb), app_data);
+    }
 
     GtkBindingSet *tv_binding_set = gtk_binding_set_by_class 
(GTK_TREE_VIEW_GET_CLASS(app_data->tree_view));
     g_signal_new ("hide-all-otps", G_TYPE_OBJECT, G_SIGNAL_RUN_FIRST | 
G_SIGNAL_ACTION, 0, NULL, NULL, NULL, G_TYPE_NONE, 0);
@@ -68,7 +89,7 @@
     // signal emitted when right-clicked on a row (shows edit/delete context 
menu)
     g_signal_connect(app_data->tree_view, "button-press-event", 
G_CALLBACK(on_treeview_button_press_event), app_data);
 
-    g_object_unref (list_store);
+    select_first_row (app_data);
 }
 
 
@@ -76,46 +97,53 @@
 update_model (AppData *app_data)
 {
     if (app_data->tree_view != NULL) {
-        GtkListStore *store = GTK_LIST_STORE(gtk_tree_view_get_model 
(app_data->tree_view));
+        GtkListStore *store = app_data->list_store;
+        if (store == NULL) {
+            return;
+        }
         gtk_list_store_clear (store);
         add_data_to_model (app_data->db_data, store);
+        if (app_data->filter_model != NULL) {
+            gtk_tree_model_filter_refilter (app_data->filter_model);
+        }
     }
 }
 
 
 void
-row_selected_cb (GtkTreeView        *tree_view,
+row_selected_cb (GtkTreeView        *tree_view UNUSED,
                  GtkTreePath        *path,
                  GtkTreeViewColumn  *column UNUSED,
                  gpointer            user_data)
 {
     CAST_USER_DATA(AppData, app_data, user_data);
     if (app_data->is_reorder_active == FALSE) {
-        GtkTreeModel *model = gtk_tree_view_get_model (tree_view);
-
         GtkTreeIter iter;
-        gtk_tree_model_get_iter (model, &iter, path);
+        if (!get_liststore_iter_from_path (app_data, path, &iter)) {
+            return;
+        }
 
-        gchar *otp_type, *otp_value;
-        gtk_tree_model_get (model, &iter, COLUMN_TYPE, &otp_type, -1);
-        gtk_tree_model_get (model, &iter, COLUMN_OTP, &otp_value, -1);
+        gchar *otp_type = NULL;
+        gchar *otp_value = NULL;
+        gtk_tree_model_get (GTK_TREE_MODEL(app_data->list_store), &iter,
+                            COLUMN_TYPE, &otp_type,
+                            COLUMN_OTP, &otp_value,
+                            -1);
 
         GDateTime *now = g_date_time_new_now_local ();
         GTimeSpan diff = g_date_time_difference (now, 
app_data->db_data->last_hotp_update);
-        if (otp_value != NULL && g_utf8_strlen (otp_value, -1) > 3) {
-            // OTP is already set, so we update the value only if it is an HOTP
-            if (g_ascii_strcasecmp (otp_type, "HOTP") == 0) {
-                if (diff >= G_USEC_PER_SEC * HOTP_RATE_LIMIT_IN_SEC) {
-                    set_otp (GTK_LIST_STORE (model), iter, app_data);
-                    g_free (otp_value);
-                    gtk_tree_model_get (model, &iter, COLUMN_OTP, &otp_value, 
-1);
-                }
-            }
-        } else {
-            // OTP is not already set, so we set it
-            set_otp (GTK_LIST_STORE (model), iter, app_data);
+        gboolean should_update = (otp_value == NULL || g_utf8_strlen 
(otp_value, -1) <= 3);
+        if (!should_update && otp_type != NULL && g_ascii_strcasecmp 
(otp_type, "HOTP") == 0) {
+            should_update = (diff >= G_USEC_PER_SEC * HOTP_RATE_LIMIT_IN_SEC);
+        }
+
+        if (should_update) {
+            set_otp (app_data->list_store, iter, app_data);
             g_free (otp_value);
-            gtk_tree_model_get (model, &iter, COLUMN_OTP, &otp_value, -1);
+            otp_value = NULL;
+            gtk_tree_model_get (GTK_TREE_MODEL(app_data->list_store), &iter,
+                                COLUMN_OTP, &otp_value,
+                                -1);
         }
         // and, in any case, we copy the otp to the clipboard and send a 
notification
         gtk_clipboard_set_text (app_data->clipboard, otp_value, -1);
@@ -138,21 +166,25 @@
     GSList *nodes_order_slist = NULL;
     GtkTreeIter iter;
     guint current_db_pos;
-    GtkTreeModel *model = gtk_tree_view_get_model (app_data->tree_view);
+    GtkTreeModel *model = GTK_TREE_MODEL(app_data->list_store);
 
     gint slist_len = 0;
     gboolean valid = gtk_tree_model_get_iter_first (model, &iter);
     while (valid) {
         GtkTreePath *path = gtk_tree_model_get_path (model, &iter);
+        if (path == NULL) {
+            valid = gtk_tree_model_iter_next(model, &iter);
+            continue;
+        }
         gtk_tree_model_get (model, &iter, COLUMN_POSITION_IN_DB, 
&current_db_pos, -1);
-        if (gtk_tree_path_get_indices (path)[0] != current_db_pos) {
+        gint *indices = gtk_tree_path_get_indices (path);
+        if (indices != NULL && indices[0] != (gint)current_db_pos) {
             NodeInfo *node_info = g_new0 (NodeInfo, 1);
             json_t *obj = json_array_get 
(app_data->db_data->in_memory_json_data, current_db_pos);
-            node_info->newpos = gtk_tree_path_get_indices (path)[0];
+            node_info->newpos = indices[0];
             node_info->hash = json_object_get_hash (obj);
-            nodes_order_slist = g_slist_append (nodes_order_slist, g_memdup2 
(node_info, sizeof (NodeInfo)));
+            nodes_order_slist = g_slist_append (nodes_order_slist, node_info);
             slist_len++;
-            g_free (node_info);
         }
         gtk_tree_path_free (path);
         valid = gtk_tree_model_iter_next(model, &iter);
@@ -174,7 +206,6 @@
                 json_decref (obj);
             }
         }
-        g_free (ni);
     }
 
     // update the database and reload the changes
@@ -197,7 +228,7 @@
     }
     regenerate_model (app_data);
 
-    g_slist_free (nodes_order_slist);
+    g_slist_free_full (nodes_order_slist, g_free);
 }
 
 
@@ -215,10 +246,9 @@
 {
     g_return_if_fail (app_data->tree_view != NULL);
 
-    GtkTreeModel *model = gtk_tree_view_get_model (app_data->tree_view);
-    GtkTreeSelection *selection = gtk_tree_view_get_selection 
(GTK_TREE_VIEW(app_data->tree_view));
+    GtkListStore *list_store = NULL;
     GtkTreeIter iter;
-    if (!gtk_tree_selection_get_selected (selection, &model, &iter)) {
+    if (!get_selected_liststore_iter (app_data, &list_store, &iter)) {
         show_message_dialog (app_data->main_window, "No row has been selected. 
Nothing will be deleted.", GTK_MESSAGE_ERROR);
         return;
     }
@@ -243,21 +273,21 @@
     }
 
     gint db_item_position_to_delete;
-    gtk_tree_model_get (model, &iter, COLUMN_POSITION_IN_DB, 
&db_item_position_to_delete, -1);
+    gtk_tree_model_get (GTK_TREE_MODEL(list_store), &iter, 
COLUMN_POSITION_IN_DB, &db_item_position_to_delete, -1);
 
     json_array_remove (app_data->db_data->in_memory_json_data, 
db_item_position_to_delete);
-    gtk_list_store_remove (GTK_LIST_STORE(model), &iter);
+    gtk_list_store_remove (list_store, &iter);
 
     // json_array_remove shifts all items, so we have to take care of updating 
the real item's position in the database
     gint row_db_pos;
-    gboolean valid = gtk_tree_model_get_iter_first(model, &iter);
+    gboolean valid = gtk_tree_model_get_iter_first 
(GTK_TREE_MODEL(list_store), &iter);
     while (valid) {
-        gtk_tree_model_get (model, &iter, COLUMN_POSITION_IN_DB, &row_db_pos, 
-1);
+        gtk_tree_model_get (GTK_TREE_MODEL(list_store), &iter, 
COLUMN_POSITION_IN_DB, &row_db_pos, -1);
         if (row_db_pos > db_item_position_to_delete) {
             gint shifted_position = row_db_pos - 1;
-            gtk_list_store_set (GTK_LIST_STORE(model), &iter, 
COLUMN_POSITION_IN_DB, shifted_position, -1);
+            gtk_list_store_set (list_store, &iter, COLUMN_POSITION_IN_DB, 
shifted_position, -1);
         }
-        valid = gtk_tree_model_iter_next(model, &iter);
+        valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(list_store), &iter);
     }
 
     GError *err = NULL;
@@ -266,12 +296,14 @@
         gchar *msg = g_strconcat ("The database update <b>FAILED</b>. The 
error message is:\n", err->message, NULL);
         show_message_dialog (app_data->main_window, msg, GTK_MESSAGE_ERROR);
         g_free (msg);
+        g_clear_error (&err);
     } else {
         reload_db (app_data->db_data, &err);
         if (err != NULL) {
             gchar *msg = g_strconcat ("The database update <b>FAILED</b>. The 
error message is:\n", err->message, NULL);
             show_message_dialog (app_data->main_window, msg, 
GTK_MESSAGE_ERROR);
             g_free (msg);
+            g_clear_error (&err);
         }
     }
 }
@@ -310,8 +342,8 @@
 
             GtkWidget *menu = gtk_menu_new ();
             GtkWidget *menu_item = gtk_menu_item_new_with_label ("Edit row");
-            g_signal_connect(menu_item, "activate", G_CALLBACK (edit_row_cb), 
app_data);
-            gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
+            g_signal_connect (menu_item, "activate", G_CALLBACK (edit_row_cb), 
app_data);
+            gtk_menu_shell_append (GTK_MENU_SHELL(menu), menu_item);
 
             menu_item = gtk_menu_item_new_with_label ("Delete row");
             g_signal_connect (menu_item, "activate", G_CALLBACK 
(on_delete_activate), app_data);
@@ -332,13 +364,16 @@
 
 
 static void
-hide_all_otps_cb (GtkTreeView *tree_view,
+hide_all_otps_cb (GtkTreeView *tree_view UNUSED,
                   gpointer     user_data)
 {
-    gtk_tree_model_foreach (GTK_TREE_MODEL(gtk_tree_view_get_model 
(tree_view)), clear_all_otps, user_data);
+    CAST_USER_DATA(AppData, app_data, user_data);
+    if (app_data->list_store == NULL) {
+        return;
+    }
+    gtk_tree_model_foreach (GTK_TREE_MODEL(app_data->list_store), 
clear_all_otps, user_data);
 }
 
-
 static gboolean
 clear_all_otps (GtkTreeModel *model,
                 GtkTreePath  *path UNUSED,
@@ -359,26 +394,140 @@
 }
 
 
+static gboolean
+filter_visible_func (GtkTreeModel *model,
+                     GtkTreeIter  *iter,
+                     gpointer      user_data)
+{
+    CAST_USER_DATA(AppData, app_data, user_data);
+    if (app_data->search_entry == NULL) {
+        return TRUE;
+    }
+
+    const gchar *query = gtk_entry_get_text 
(GTK_ENTRY(app_data->search_entry));
+    if (query == NULL || *query == '\0') {
+        return TRUE;
+    }
+
+    gchar *query_folded = g_utf8_strdown (query, -1);
+    gboolean match = row_matches_query (model, iter, query_folded);
+    g_free (query_folded);
+
+    return match;
+}
+
+
+static gboolean
+row_matches_query (GtkTreeModel *model,
+                   GtkTreeIter  *iter,
+                   const gchar  *query_folded)
+{
+    gchar *type = NULL;
+    gchar *label = NULL;
+    gchar *issuer = NULL;
+    gtk_tree_model_get (model, iter,
+                        COLUMN_TYPE, &type,
+                        COLUMN_ACC_LABEL, &label,
+                        COLUMN_ACC_ISSUER, &issuer,
+                        -1);
+
+    gboolean match = FALSE;
+    if (type != NULL) {
+        gchar *type_folded = g_utf8_strdown (type, -1);
+        match = (g_strstr_len (type_folded, -1, query_folded) != NULL);
+        g_free (type_folded);
+    }
+
+    if (!match && label != NULL) {
+        gchar *label_folded = g_utf8_strdown (label, -1);
+        match = (g_strstr_len (label_folded, -1, query_folded) != NULL);
+        g_free (label_folded);
+    }
+
+    if (!match && issuer != NULL) {
+        gchar *issuer_folded = g_utf8_strdown (issuer, -1);
+        match = (g_strstr_len (issuer_folded, -1, query_folded) != NULL);
+        g_free (issuer_folded);
+    }
+
+    g_free (type);
+    g_free (label);
+    g_free (issuer);
+
+    return match;
+}
+
+static void
+search_entry_changed_cb (GtkEntry *entry UNUSED,
+                         gpointer  user_data)
+{
+    CAST_USER_DATA(AppData, app_data, user_data);
+    if (app_data->filter_model != NULL) {
+        gtk_tree_model_filter_refilter (app_data->filter_model);
+    }
+    select_first_row (app_data);
+}
+
 static void
-set_json_data (json_t     *array,
-               ParsedData *pjd)
+search_entry_activate_cb (GtkEntry *entry UNUSED,
+                          gpointer  user_data)
 {
-    gsize array_len = json_array_size (array);
-    pjd->types = (gchar **)g_malloc0 ((array_len + 1)  * sizeof(gchar *));
-    pjd->labels = (gchar **)g_malloc0 ((array_len + 1) * sizeof(gchar *));
-    pjd->issuers = (gchar **)g_malloc0 ((array_len + 1) * sizeof(gchar *));
-    pjd->periods = g_array_new (FALSE, FALSE, sizeof(gint));
-    for (guint i = 0; i < array_len; i++) {
-        json_t *obj = json_array_get (array, i);
-        pjd->types[i] = g_strdup (json_string_value (json_object_get (obj, 
"type")));
-        pjd->labels[i] = g_strdup (json_string_value (json_object_get (obj, 
"label")));
-        pjd->issuers[i] = g_strdup (json_string_value (json_object_get (obj, 
"issuer")));
-        json_int_t period = json_integer_value (json_object_get (obj, 
"period"));
-        g_array_append_val (pjd->periods, period);
-    }
-    pjd->types[array_len] = NULL;
-    pjd->labels[array_len] = NULL;
-    pjd->issuers[array_len] = NULL;
+    CAST_USER_DATA(AppData, app_data, user_data);
+    GtkTreeModel *model = GTK_TREE_MODEL(app_data->filter_model);
+    GtkTreeSelection *selection = gtk_tree_view_get_selection 
(app_data->tree_view);
+    if (model == NULL) {
+        return;
+    }
+
+    if (gtk_tree_selection_count_selected_rows (selection) == 0) {
+        select_first_row (app_data);
+    }
+
+    GList *paths = gtk_tree_selection_get_selected_rows (selection, &model);
+    if (paths != NULL) {
+        GtkTreePath *path = g_list_first (paths)->data;
+        GtkTreeViewColumn *column = gtk_tree_view_get_column 
(app_data->tree_view, 0);
+        gtk_tree_view_row_activated (app_data->tree_view, path, column);
+        g_list_free_full (paths, (GDestroyNotify)gtk_tree_path_free);
+    }
+}
+
+static void
+select_first_row (AppData *app_data)
+{
+    if (app_data->filter_model == NULL) {
+        return;
+    }
+    GtkTreeIter iter;
+    GtkTreeModel *model = GTK_TREE_MODEL(app_data->filter_model);
+    GtkTreeSelection *selection = gtk_tree_view_get_selection 
(app_data->tree_view);
+    gtk_tree_selection_unselect_all (selection);
+    if (gtk_tree_model_get_iter_first (model, &iter)) {
+        GtkTreePath *path = gtk_tree_model_get_path (model, &iter);
+        gtk_tree_selection_select_path (selection, path);
+        gtk_tree_view_scroll_to_cell (app_data->tree_view, path, NULL, FALSE, 
0.0f, 0.0f);
+        gtk_tree_path_free (path);
+    }
+}
+
+static gboolean
+get_liststore_iter_from_path (AppData     *app_data,
+                              GtkTreePath *path,
+                              GtkTreeIter *iter)
+{
+    GtkTreeModel *model = gtk_tree_view_get_model (app_data->tree_view);
+    GtkTreeIter view_iter;
+    if (!gtk_tree_model_get_iter (model, &view_iter, path)) {
+        return FALSE;
+    }
+
+    if (GTK_IS_TREE_MODEL_FILTER (model)) {
+        gtk_tree_model_filter_convert_iter_to_child_iter 
(GTK_TREE_MODEL_FILTER(model), iter, &view_iter);
+        return TRUE;
+    }
+
+    *iter = view_iter;
+    return TRUE;
 }
 
 
@@ -386,26 +535,31 @@
 add_data_to_model (DatabaseData *db_data,
                    GtkListStore *store)
 {
-    GtkTreeIter iter;
-    ParsedData *pjd = g_new0 (ParsedData, 1);
+    if (db_data == NULL || store == NULL || db_data->in_memory_json_data == 
NULL) {
+        return;
+    }
 
-    set_json_data (db_data->in_memory_json_data, pjd);
+    gsize array_len = json_array_size (db_data->in_memory_json_data);
+    for (guint i = 0; i < array_len; i++) {
+        json_t *obj = json_array_get (db_data->in_memory_json_data, i);
+        const gchar *type = json_get_string_or_empty (obj, "type");
+        const gchar *label = json_get_string_or_empty (obj, "label");
+        const gchar *issuer = json_get_string_or_empty (obj, "issuer");
+        json_t *period_value = json_object_get (obj, "period");
+        gint period = json_is_integer (period_value) ? 
(gint)json_integer_value (period_value) : 0;
 
-    gint i = 0;
-    while (pjd->types[i] != NULL) {
+        GtkTreeIter iter;
         gtk_list_store_append (store, &iter);
         gtk_list_store_set (store, &iter,
-                            COLUMN_TYPE, pjd->types[i],
-                            COLUMN_ACC_LABEL, pjd->labels[i],
-                            COLUMN_ACC_ISSUER, pjd->issuers[i],
-                            COLUMN_PERIOD, g_array_index (pjd->periods, gint, 
i),
+                            COLUMN_TYPE, type,
+                            COLUMN_ACC_LABEL, label,
+                            COLUMN_ACC_ISSUER, issuer,
+                            COLUMN_PERIOD, period,
                             COLUMN_UPDATED, FALSE,
                             COLUMN_LESS_THAN_A_MINUTE, FALSE,
                             COLUMN_POSITION_IN_DB, i,
                             -1);
-        i++;
     }
-    free_pjd (pjd);
 }
 
 
@@ -440,12 +594,15 @@
 }
 
 
-static void
-free_pjd (ParsedData *pjd)
+static const gchar *
+json_get_string_or_empty (json_t      *obj,
+                          const gchar *key)
 {
-    g_strfreev (pjd->types);
-    g_strfreev (pjd->labels);
-    g_strfreev (pjd->issuers);
-    g_array_free (pjd->periods, TRUE);
-    g_free (pjd);
+    if (obj == NULL || key == NULL) {
+        return "";
+    }
+
+    json_t *value = json_object_get (obj, key);
+    const gchar *string_value = json_string_value (value);
+    return string_value != NULL ? string_value : "";
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/OTPClient-4.1.0/src/gui/ui/otpclient.ui 
new/OTPClient-4.2.0/src/gui/ui/otpclient.ui
--- old/OTPClient-4.1.0/src/gui/ui/otpclient.ui 2025-05-07 15:52:20.000000000 
+0200
+++ new/OTPClient-4.2.0/src/gui/ui/otpclient.ui 2025-12-23 13:40:00.000000000 
+0100
@@ -1237,6 +1237,19 @@
         <property name="can-focus">False</property>
         <property name="orientation">vertical</property>
         <child>
+          <object class="GtkSearchEntry" id="search_entry_id">
+            <property name="visible">False</property>
+            <property name="can-focus">True</property>
+            <property name="hexpand">True</property>
+            <property name="placeholder-text" 
translatable="yes">Search</property>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">True</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
           <object class="GtkScrolledWindow" id="scrolledwin_id">
             <property name="visible">True</property>
             <property name="can-focus">True</property>
@@ -2020,17 +2033,6 @@
               </packing>
             </child>
             <child>
-              <object class="GtkLabel">
-                <property name="visible">True</property>
-                <property name="can-focus">False</property>
-                <property name="label" translatable="yes">Search by</property>
-              </object>
-              <packing>
-                <property name="left-attach">0</property>
-                <property name="top-attach">2</property>
-              </packing>
-            </child>
-            <child>
               <object class="GtkSwitch" id="notif_switch_id">
                 <property name="visible">True</property>
                 <property name="can-focus">True</property>
@@ -2043,22 +2045,6 @@
               </packing>
             </child>
             <child>
-              <object class="GtkComboBoxText" id="search_by_cb_id">
-                <property name="visible">True</property>
-                <property name="can-focus">False</property>
-                <property name="active">0</property>
-                <property name="active-id">0</property>
-                <items>
-                  <item id="0" translatable="yes">Account</item>
-                  <item id="1" translatable="yes">Issuer</item>
-                </items>
-              </object>
-              <packing>
-                <property name="left-attach">1</property>
-                <property name="top-attach">2</property>
-              </packing>
-            </child>
-            <child>
               <object class="GtkSwitch" id="nextotp_switch_id">
                 <property name="visible">True</property>
                 <property name="can-focus">True</property>

Reply via email to