Skip to content

Commit

Permalink
Add rows reordering feature
Browse files Browse the repository at this point in the history
this finally fixes #184
  • Loading branch information
paolostivanin committed Mar 30, 2022
1 parent 083d586 commit 6ac0c17
Show file tree
Hide file tree
Showing 8 changed files with 186 additions and 57 deletions.
43 changes: 36 additions & 7 deletions src/app.c
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,12 @@ static void get_window_size_cb (GtkWidget *window,
GtkAllocation *allocation,
gpointer user_data);

static void toggle_delete_button_cb (GtkWidget *main_window,
static void toggle_button_cb (GtkWidget *main_window,
gpointer user_data);

static void reorder_rows_cb (GtkToggleButton *btn,
gpointer user_data);

static void del_data_cb (GtkToggleButton *btn,
gpointer user_data);

Expand Down Expand Up @@ -89,6 +92,7 @@ activate (GtkApplication *app,
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->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;
get_wh_data (&width, &height, app_data);
Expand Down Expand Up @@ -221,12 +225,19 @@ activate (GtkApplication *app,
g_notification_set_body (app_data->notification, "OTP value has been copied to the clipboard");
g_object_unref (icon);

GtkToggleButton *del_toggle_btn = GTK_TOGGLE_BUTTON(gtk_builder_get_object (app_data->builder, "del_toggle_btn_id"));
GtkBindingSet *binding_set = gtk_binding_set_by_class (GTK_APPLICATION_WINDOW_GET_CLASS (app_data->main_window));

GtkToggleButton *reorder_toggle_btn = GTK_TOGGLE_BUTTON(gtk_builder_get_object (app_data->builder, "reorder_toggle_btn_id"));
g_signal_new ("toggle-reorder-button", G_TYPE_OBJECT, G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION, 0, NULL, NULL, NULL, G_TYPE_NONE, 0);
gtk_binding_entry_add_signal (binding_set, GDK_KEY_r, GDK_CONTROL_MASK, "toggle-reorder-button", 0);
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);

GtkToggleButton *del_toggle_btn = GTK_TOGGLE_BUTTON(gtk_builder_get_object (app_data->builder, "del_toggle_btn_id"));
g_signal_new ("toggle-delete-button", G_TYPE_OBJECT, G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION, 0, NULL, NULL, NULL, G_TYPE_NONE, 0);
GtkBindingSet *toggle_btn_binding_set = gtk_binding_set_by_class (GTK_APPLICATION_WINDOW_GET_CLASS (app_data->main_window));
gtk_binding_entry_add_signal (toggle_btn_binding_set, GDK_KEY_d, GDK_CONTROL_MASK, "toggle-delete-button", 0);
g_signal_connect (app_data->main_window, "toggle-delete-button", G_CALLBACK(toggle_delete_button_cb), del_toggle_btn);
gtk_binding_entry_add_signal (binding_set, GDK_KEY_d, GDK_CONTROL_MASK, "toggle-delete-button", 0);
g_signal_connect (app_data->main_window, "toggle-delete-button", G_CALLBACK(toggle_button_cb), del_toggle_btn);
g_signal_connect (del_toggle_btn, "toggled", G_CALLBACK(del_data_cb), app_data);
g_signal_connect (app_data->main_window, "key_press_event", G_CALLBACK(key_pressed_cb), NULL);

Expand Down Expand Up @@ -508,13 +519,31 @@ get_db_path (AppData *app_data)


static void
toggle_delete_button_cb (GtkWidget *main_window __attribute__((unused)),
gpointer user_data)
toggle_button_cb (GtkWidget *main_window __attribute__((unused)),
gpointer user_data)
{
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(user_data), !gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(user_data)));
}


static void
reorder_rows_cb (GtkToggleButton *btn,
gpointer user_data)
{
AppData *app_data = (AppData *)user_data;
gboolean is_btn_active = gtk_toggle_button_get_active (btn);
gtk_tree_view_set_reorderable (GTK_TREE_VIEW(app_data->tree_view), is_btn_active);
app_data->is_reorder_active = is_btn_active;
gtk_widget_set_sensitive (GTK_WIDGET(gtk_builder_get_object (app_data->builder, "add_btn_main_id")), !is_btn_active);
gtk_widget_set_sensitive (GTK_WIDGET(gtk_builder_get_object (app_data->builder, "del_toggle_btn_id")), !is_btn_active);

if (is_btn_active == FALSE) {
// reordering has been disabled, so now we have to reorder and update the database itself
reorder_db (app_data);
}
}


static void
del_data_cb (GtkToggleButton *btn,
gpointer user_data)
Expand Down
12 changes: 10 additions & 2 deletions src/data.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

G_BEGIN_DECLS

typedef struct _db_data {
typedef struct db_data_t {
gchar *db_path;

gchar *key;
Expand All @@ -25,7 +25,7 @@ typedef struct _db_data {
} DatabaseData;


typedef struct _app_data_t {
typedef struct app_data_t {
GtkBuilder *builder;

GtkWidget *main_window;
Expand Down Expand Up @@ -55,10 +55,18 @@ typedef struct _app_data_t {

gboolean use_dark_theme;

gboolean is_reorder_active;

GDateTime *last_user_activity;

GtkWidget *diag_rcdb;
GtkFileChooserAction open_db_file_action;
} AppData;


typedef struct node_info_t {
guint hash;
guint newpos;
} NodeInfo;

G_END_DECLS
2 changes: 1 addition & 1 deletion src/imports.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ G_BEGIN_DECLS
#define FREEOTPPLUS_IMPORT_ACTION_NAME "import_freeotpplus"
#define AEGIS_IMPORT_ACTION_NAME "import_aegis"

typedef struct _otp_t {
typedef struct otp_object_t {
gchar *type;

gchar *algo;
Expand Down
2 changes: 1 addition & 1 deletion src/liststore-misc.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
#include "common/common.h"


typedef struct _otp_data {
typedef struct otp_data_t {
gchar *type;
gchar *secret;
gchar *algo;
Expand Down
119 changes: 91 additions & 28 deletions src/treeview.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@
#include "otpclient.h"
#include "liststore-misc.h"
#include "message-dialogs.h"
#include "common/common.h"


typedef struct _parsed_json_data {
typedef struct parsed_json_data_t {
gchar **types;
gchar **labels;
gchar **issuers;
Expand Down Expand Up @@ -125,41 +126,103 @@ row_selected_cb (GtkTreeView *tree_view,
gpointer user_data)
{
AppData *app_data = (AppData *)user_data;
GtkTreeModel *model = gtk_tree_view_get_model (tree_view);
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);
GtkTreeIter iter;
gtk_tree_model_get_iter (model, &iter, path);

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);

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);
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);

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);
g_free (otp_value);
gtk_tree_model_get (model, &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);
if (!app_data->disable_notifications) {
g_application_send_notification (G_APPLICATION(gtk_window_get_application (GTK_WINDOW (app_data->main_window))), NOTIFICATION_ID,
app_data->notification);
}
} else {
// OTP is not already set, so we set it
set_otp (GTK_LIST_STORE (model), iter, app_data);

g_date_time_unref (now);
g_free (otp_type);
g_free (otp_value);
gtk_tree_model_get (model, &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);
if (!app_data->disable_notifications) {
g_application_send_notification (G_APPLICATION(gtk_window_get_application (GTK_WINDOW(app_data->main_window))), NOTIFICATION_ID, app_data->notification);
}


void
reorder_db (AppData *app_data)
{
// Iter through all rows. If the position in treeview is different from current_db_pos, then compute hash and add (hash,newpos) to the list
GSList *nodes_order_slist = NULL;
GtkTreeIter iter;
guint current_db_pos;
GtkTreeModel *model = gtk_tree_view_get_model (app_data->tree_view);

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);
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) {
NodeInfo *node_info = g_new0 (NodeInfo, 1);
json_t *obj = json_array_get (app_data->db_data->json_data, current_db_pos);
node_info->newpos = gtk_tree_path_get_indices (path)[0];
node_info->hash = json_object_get_hash (obj);
nodes_order_slist = g_slist_append (nodes_order_slist, g_memdupX (node_info, sizeof (NodeInfo)));
slist_len++;
g_free (node_info);
}
gtk_tree_path_free (path);
valid = gtk_tree_model_iter_next(model, &iter);
}

g_date_time_unref (now);
g_free (otp_type);
g_free (otp_value);
// move the reordered items to their new position in the database
gsize index;
json_t *obj;
for (gint i = 0; i < slist_len; i++) {
NodeInfo *ni = g_slist_nth_data (nodes_order_slist, i);
json_array_foreach (app_data->db_data->json_data, index, obj) {
guint32 db_obj_hash = json_object_get_hash (obj);
if (db_obj_hash == ni->hash) {
// remove the obj from the current position...
json_incref (obj);
json_array_remove (app_data->db_data->json_data, index);
// ...and add it to the desired one
json_array_insert (app_data->db_data->json_data, ni->newpos, obj);
json_decref (obj);
}
}
g_free (ni);
}

// update the database and reload the changes
GError *err = NULL;
update_and_reload_db (app_data, app_data->db_data, TRUE, &err);
if (err != NULL) {
gchar *msg = g_strconcat ("[ERROR] Failed to update_and_reload_db: ", err->message, NULL);
show_message_dialog (app_data->main_window, msg, GTK_MESSAGE_ERROR);
g_free (msg);
g_clear_error (&err);
}
g_slist_free (nodes_order_slist);
}


Expand Down
22 changes: 12 additions & 10 deletions src/treeview.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,20 @@ enum {
NUM_COLUMNS
};

void create_treeview (AppData *app_data);
void create_treeview (AppData *app_data);

void update_model (AppData *app_data);
void update_model (AppData *app_data);

void delete_rows_cb (GtkTreeView *tree_view,
GtkTreePath *path,
GtkTreeViewColumn *column,
gpointer user_data);
void delete_rows_cb (GtkTreeView *tree_view,
GtkTreePath *path,
GtkTreeViewColumn *column,
gpointer user_data);

void row_selected_cb (GtkTreeView *tree_view,
GtkTreePath *path,
GtkTreeViewColumn *column,
gpointer user_data);
void row_selected_cb (GtkTreeView *tree_view,
GtkTreePath *path,
GtkTreeViewColumn *column,
gpointer user_data);

void reorder_db (AppData *app_data);

G_END_DECLS
Loading

0 comments on commit 6ac0c17

Please sign in to comment.