/* * Copyright (C) 2005 Kouji TAKAO * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Library General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifdef HAVE_CONFIG_H # include #endif #include #include #include #include "gpass/root-entry.h" #include "gpass/error.h" #include "helper.h" #include "application.h" #include "summary.h" #include "entry-tree.h" enum { COLUMN_ICON, COLUMN_NAME, COLUMN_ENTRY, NUM_COLUMNS }; static void entry_tree_on_selection_changed(GtkWidget *widget, gpointer user_data); static gboolean (*parent_drag_data_received)(GtkTreeDragDest *drag_dest, GtkTreePath *dest, GtkSelectionData *selection_data); static gboolean drag_dest_drag_data_received(GtkTreeDragDest *drag_dest, GtkTreePath *dest, GtkSelectionData *selection_data) { GtkTreeModel *src_model; GtkTreePath *src_path; GtkTreeModel *tree_model = GTK_TREE_MODEL(drag_dest); GtkTreeIter iter; GtkTreePath *up; GPassEntry *target, *parent, *previous; GPassWindow *window; if (!gtk_tree_get_row_drag_data(selection_data, &src_model, &src_path) && src_model != tree_model) { return FALSE; } if (gtk_tree_path_compare(src_path, dest) == 0) { gtk_tree_path_free(src_path); return FALSE; } if (gtk_tree_model_get_iter(src_model, &iter, src_path)) { gtk_tree_model_get(tree_model, &iter, COLUMN_ENTRY, &target, -1); } gtk_tree_path_free(src_path); up = gtk_tree_path_copy(dest); if (gtk_tree_path_up(up) && gtk_tree_path_get_depth(up) > 0) { GPassEntryClass *klass; if (!gtk_tree_model_get_iter(tree_model, &iter, up)) { gtk_tree_path_free(up); return FALSE; } gtk_tree_model_get(tree_model, &iter, COLUMN_ENTRY, &parent, -1); klass = GPASS_ENTRY_GET_CLASS(parent); if (gpass_entry_class_can_have_child(klass)) { if (gtk_tree_path_prev(dest) && gtk_tree_model_get_iter(tree_model, &iter, dest)) { gtk_tree_model_get(tree_model, &iter, COLUMN_ENTRY, &previous, -1); } else { previous = NULL; } } else { previous = gpass_entry_prev_sibling(parent); parent = gpass_entry_parent(parent); } } else { /* dest is top level entry */ parent = gpass_entry_root(target); if (gtk_tree_path_prev(dest) && gtk_tree_model_get_iter(tree_model, &iter, dest)) { gtk_tree_model_get(tree_model, &iter, COLUMN_ENTRY, &previous, -1); } else { previous = NULL; } } gtk_tree_path_free(up); if (previous == target) { return FALSE; } window = GPASS_WINDOW(g_object_get_data(G_OBJECT(tree_model), "window")); gpass_entry_tree_push_move_command(window, target, parent, previous); return TRUE; } static void setup_drag_and_drop(GtkTreeModel *model) { GtkTreeDragDestIface *drag_dest_iface = GTK_TREE_DRAG_DEST_GET_IFACE(model); parent_drag_data_received = drag_dest_iface->drag_data_received; drag_dest_iface->drag_data_received = drag_dest_drag_data_received; } static gboolean entry_tree_search_equal_func(GtkTreeModel *model, gint column, const gchar *key, GtkTreeIter *iter, gpointer search_data) { GPassEntry *entry; gtk_tree_model_get(model, iter, COLUMN_ENTRY, &entry, -1); if (entry == NULL) { return TRUE; } return !gpass_entry_include(entry, key); } GError * gpass_entry_tree_initialize(GPassWindow *window) { GtkTreeView *entry_tree; GtkTreeSelection *selection; GtkTreeViewColumn *column; GtkCellRenderer *renderer; GtkTreeStore *tree_store; entry_tree = GTK_TREE_VIEW (glade_xml_get_widget(GPASS_VIEW(window)->xml, "entry-tree")); renderer = gtk_cell_renderer_pixbuf_new(); column = gtk_tree_view_column_new_with_attributes (_("Icon"), renderer, "stock-id", COLUMN_ICON, NULL); gtk_tree_view_append_column(entry_tree, column); renderer = gtk_cell_renderer_text_new(); column = gtk_tree_view_column_new_with_attributes (_("Name"), renderer, "text", COLUMN_NAME, NULL); gtk_tree_view_append_column(entry_tree, column); tree_store = gtk_tree_store_new(NUM_COLUMNS, G_TYPE_STRING, /* icon */ G_TYPE_STRING, /* name */ G_TYPE_OBJECT); /* entry */ g_object_set_data(G_OBJECT(tree_store), "window", window); setup_drag_and_drop(GTK_TREE_MODEL(tree_store)); gtk_tree_view_set_model(entry_tree, GTK_TREE_MODEL(tree_store)); g_object_unref(tree_store); gtk_tree_view_set_enable_search(entry_tree, TRUE); gtk_tree_view_set_search_equal_func(entry_tree, entry_tree_search_equal_func, window, NULL); gtk_tree_view_set_reorderable(entry_tree, TRUE); selection = gtk_tree_view_get_selection(entry_tree); g_signal_connect(selection, "changed", G_CALLBACK(entry_tree_on_selection_changed), window); g_signal_connect(entry_tree, "realize", G_CALLBACK(gtk_tree_view_expand_all), NULL); return NULL; } static GError * entry_tree_build(GPassEntry *entries, GtkTreeStore *tree_store, GtkTreeIter *parent) { GPassEntry *p; for (p = entries; p != NULL; p = gpass_entry_next_sibling(p)) { GtkTreeIter iter; gchar *name; const gchar *icon_id; GError *error = NULL; gtk_tree_store_append(tree_store, &iter, parent); g_object_get(p, "name", &name, NULL); icon_id = gpass_entry_class_icon_id(GPASS_ENTRY_GET_CLASS(p)); gtk_tree_store_set(tree_store, &iter, COLUMN_ICON, icon_id, COLUMN_NAME, name, COLUMN_ENTRY, p, -1); if (gpass_entry_first_child(p) != NULL) { error = entry_tree_build (gpass_entry_first_child(p), tree_store, &iter); if (error != NULL) { return error; } } } return NULL; } GError * gpass_entry_tree_build(GPassWindow *window) { GPassApplication *app; GtkTreeView *entry_tree; GtkTreeStore *tree_store; GError *error; app = GPASS_APPLICATION(GPASS_VIEW(window)->model); entry_tree = GTK_TREE_VIEW (glade_xml_get_widget(GPASS_VIEW(window)->xml, "entry-tree")); tree_store = GTK_TREE_STORE(gtk_tree_view_get_model(entry_tree)); g_object_ref(tree_store); gtk_tree_view_set_model(entry_tree, NULL); error = entry_tree_build(gpass_entry_first_child(app->passwords), tree_store, NULL); gtk_tree_view_set_model(entry_tree, GTK_TREE_MODEL(tree_store)); g_object_unref(tree_store); return error; } void create_path_from_node(GPassEntry *entry, GString **path) { GString *result; result = g_string_new(""); while (entry != NULL) { GPassEntry *p; gint n = 0; gchar *buf; for (p = gpass_entry_prev_sibling(entry); p != NULL; p = gpass_entry_prev_sibling(p)) { n++; } buf = g_strdup(result->str); g_string_printf(result, "%d%s", n, buf); g_free(buf); entry = gpass_entry_parent(entry); if (GPASS_IS_ROOT_ENTRY(entry)) { break; } else { result = g_string_prepend_c(result, ':'); } } *path = result; } static void entry_tree_select(GtkTreeView *view, GtkTreeIter *iter) { GtkTreeSelection *selection; GtkTreeModel *model; GtkTreePath *path; selection = gtk_tree_view_get_selection(view); gtk_tree_selection_select_iter(selection, iter); model = gtk_tree_view_get_model(view); path = gtk_tree_model_get_path(model, iter); gtk_tree_view_scroll_to_cell(view, path, NULL, FALSE, 0.0, 0.0); gtk_tree_path_free(path); } void gpass_entry_tree_push_insert_command(GPassWindow *window, GPassEntry *entry, const gchar *description) { GPassApplication *app; GPassCommand *command; GPassEntry *target, *parent, *sibling; GtkWidget *entry_tree; GtkTreeModel *model; GtkTreeSelection *selection; GtkTreeIter iter; const gchar *id; gchar *msg; GError *error; target = entry; entry_tree = glade_xml_get_widget(GPASS_VIEW(window)->xml, "entry-tree"); app = GPASS_APPLICATION(GPASS_VIEW(window)->model); parent = app->passwords; selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(entry_tree)); if (gtk_tree_selection_get_selected(selection, &model, &iter)) { gtk_tree_model_get(model, &iter, COLUMN_ENTRY, &sibling, -1); parent = gpass_entry_parent(sibling); sibling = gpass_entry_next_sibling(sibling); } else { sibling = NULL; } command = gpass_insert_command_new(app, target, parent, sibling); g_object_get(entry, "id", &id, NULL); msg = g_strdup_printf(description, id); g_object_set(command, "description", msg, NULL); g_free(msg); gpass_command_stack_push(app->command_stack, command); error = gpass_command_stack_redo(app->command_stack); if (error != NULL) { gpass_helper_error(error); } gpass_window_undo_redo_sensitive(window); } void gpass_entry_tree_insert(GPassWindow *window, GPassEntry *target, GPassEntry *parent, GPassEntry *sibling) { GPassApplication *app; GtkWidget *entry_tree; GtkTreeModel *tree_model; GtkTreeStore *tree_store; GtkTreeIter target_iter, parent_iter, sibling_iter; GtkTreeIter *parent_iter_ptr, *sibling_iter_ptr; GString *path; gchar *name; const gchar *icon_id; entry_tree = glade_xml_get_widget(GPASS_VIEW(window)->xml, "entry-tree"); tree_model = gtk_tree_view_get_model(GTK_TREE_VIEW(entry_tree)); parent_iter_ptr = sibling_iter_ptr = NULL; app = GPASS_APPLICATION(GPASS_VIEW(window)->model); if (sibling != NULL) { create_path_from_node(sibling, &path); gtk_tree_model_get_iter_from_string(tree_model, &sibling_iter, path->str); sibling_iter_ptr = &sibling_iter; g_string_free(path, TRUE); } else if (!GPASS_IS_ROOT_ENTRY(parent)) { create_path_from_node(parent, &path); gtk_tree_model_get_iter_from_string(tree_model, &parent_iter, path->str); parent_iter_ptr = &parent_iter; g_string_free(path, TRUE); } tree_store = GTK_TREE_STORE(tree_model); gtk_tree_store_insert_before(tree_store, &target_iter, parent_iter_ptr, sibling_iter_ptr); g_object_get(target, "name", &name, NULL); icon_id = gpass_entry_class_icon_id(GPASS_ENTRY_GET_CLASS(target)); gtk_tree_store_set(tree_store, &target_iter, COLUMN_ICON, icon_id, COLUMN_NAME, name, COLUMN_ENTRY, target, -1); entry_tree_select(GTK_TREE_VIEW(entry_tree), &target_iter); } void gpass_entry_tree_push_unlink_command(GPassWindow *window, const gchar *description) { GPassApplication *app; GtkWidget *entry_tree; GtkTreeSelection *selection; GtkTreeModel *model; GtkTreeIter iter; GPassEntry *target; GPassCommand *command; const gchar *name; gchar *msg; GError *error; app = GPASS_APPLICATION(GPASS_VIEW(window)->model); entry_tree = glade_xml_get_widget(GPASS_VIEW(window)->xml, "entry-tree"); selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(entry_tree)); if (!gtk_tree_selection_get_selected(selection, &model, &iter)) { return; } gtk_tree_model_get(model, &iter, COLUMN_ENTRY, &target, -1); app = GPASS_APPLICATION(GPASS_VIEW(window)->model); command = gpass_unlink_command_new(app, target, gpass_entry_parent(target), gpass_entry_next_sibling(target)); g_object_get(target, "name", &name, NULL); msg = g_strdup_printf(description, name); g_object_set(command, "description", msg, NULL); g_free(msg); gpass_command_stack_push(app->command_stack, command); error = gpass_command_stack_redo(app->command_stack); if (error != NULL) { gpass_helper_error(error); } gpass_window_undo_redo_sensitive(window); gpass_window_status_say(window, _("Removed `%s'."), name); } void gpass_entry_tree_unlink(GPassWindow *window, GPassEntry *target, GPassEntry *parent, GPassEntry *sibling) { GtkWidget *entry_tree; GtkTreeModel *tree_model; GtkTreeIter iter; GString *path; entry_tree = glade_xml_get_widget(GPASS_VIEW(window)->xml, "entry-tree"); tree_model = gtk_tree_view_get_model(GTK_TREE_VIEW(entry_tree)); create_path_from_node(target, &path); gtk_tree_model_get_iter_from_string(tree_model, &iter, path->str); g_string_free(path, TRUE); gtk_tree_store_remove(GTK_TREE_STORE(tree_model), &iter); } void gpass_entry_tree_push_move_command(GPassWindow *window, GPassEntry *target, GPassEntry *parent, GPassEntry *previous) { GPassApplication *app; GPassCommand *command; GtkWidget *entry_tree; const gchar *name; GError *error; entry_tree = glade_xml_get_widget(GPASS_VIEW(window)->xml, "entry-tree"); app = GPASS_APPLICATION(GPASS_VIEW(window)->model); command = gpass_move_command_new(app, target, parent, previous); gpass_command_stack_push(app->command_stack, command); error = gpass_command_stack_redo(app->command_stack); if (error != NULL) { gpass_helper_error(error); } gpass_window_undo_redo_sensitive(window); g_object_get(target, "name", &name, NULL); gpass_window_status_say(window, _("Moved `%s'."), name); } void gpass_entry_tree_move_after(GPassWindow *window, GPassEntry *target, GPassEntry *parent, GPassEntry *previous) { GtkTreeView *entry_tree; GtkTreeStore *tree_store; GString *path; GtkTreeIter target_iter, previous_iter, parent_iter, new_iter; GtkTreeIter *previous_iter_ptr, *parent_iter_ptr; gchar *name; const gchar *icon_id; GPassEntry *entry; entry_tree = GTK_TREE_VIEW (glade_xml_get_widget(GPASS_VIEW(window)->xml, "entry-tree")); tree_store = GTK_TREE_STORE(gtk_tree_view_get_model(entry_tree)); create_path_from_node(target, &path); gtk_tree_model_get_iter_from_string(GTK_TREE_MODEL(tree_store), &target_iter, path->str); g_string_free(path, TRUE); if (previous == NULL) { previous_iter_ptr = NULL; } else { create_path_from_node(previous, &path); gtk_tree_model_get_iter_from_string(GTK_TREE_MODEL(tree_store), &previous_iter, path->str); g_string_free(path, TRUE); previous_iter_ptr = &previous_iter; } if (GPASS_IS_ROOT_ENTRY(parent)) { parent_iter_ptr = NULL; } else { create_path_from_node(parent, &path); gtk_tree_model_get_iter_from_string(GTK_TREE_MODEL(tree_store), &parent_iter, path->str); g_string_free(path, TRUE); parent_iter_ptr = &parent_iter; } gtk_tree_model_get(GTK_TREE_MODEL(tree_store), &target_iter, COLUMN_ENTRY, &entry, -1); /* build entry tree */ gtk_tree_store_insert_after(tree_store, &new_iter, parent_iter_ptr, previous_iter_ptr); g_object_get(entry, "name", &name, NULL); icon_id = gpass_entry_class_icon_id(GPASS_ENTRY_GET_CLASS(entry)); gtk_tree_store_set(tree_store, &new_iter, COLUMN_ICON, icon_id, COLUMN_NAME, name, COLUMN_ENTRY, entry, -1); if (gpass_entry_has_child(entry)) { GError *error; error = entry_tree_build(gpass_entry_first_child(entry), tree_store, &new_iter); if (error != NULL) { gpass_error_show_and_exit(error); } } { GtkTreePath *path = gtk_tree_model_get_path(GTK_TREE_MODEL(tree_store), &target_iter); if (gtk_tree_view_row_expanded(entry_tree, path)) { GtkTreePath *new_path = gtk_tree_model_get_path(GTK_TREE_MODEL(tree_store), &new_iter); gtk_tree_view_expand_row(entry_tree, new_path, TRUE); gtk_tree_path_free(new_path); } gtk_tree_path_free(path); } gtk_tree_store_remove(tree_store, &target_iter); entry_tree_select(GTK_TREE_VIEW(entry_tree), &new_iter); } void gpass_entry_tree_change_entry(GPassWindow *window, GPassEntry *target, GPassEntry *entry) { GtkWidget *entry_tree; GtkTreeModel *tree_model; GtkTreeIter iter; GString *path; const gchar *name; const gchar *icon_id; entry_tree = glade_xml_get_widget(GPASS_VIEW(window)->xml, "entry-tree"); tree_model = gtk_tree_view_get_model(GTK_TREE_VIEW(entry_tree)); create_path_from_node(target, &path); gtk_tree_model_get_iter_from_string(tree_model, &iter, path->str); g_string_free(path, TRUE); g_object_get(entry, "name", &name, NULL); icon_id = gpass_entry_class_icon_id(GPASS_ENTRY_GET_CLASS(entry)); gtk_tree_store_set(GTK_TREE_STORE(tree_model), &iter, COLUMN_ICON, icon_id, COLUMN_NAME, name, COLUMN_ENTRY, entry, -1); } /*********************************************************** * * Signal handler * ***********************************************************/ static void entry_tree_on_selection_changed(GtkWidget *widget, gpointer user_data) { GPassWindow *window = GPASS_WINDOW(user_data); GtkTreeView *entry_tree; GtkTreeSelection *selection; GtkTreeModel *model; GtkTreeIter iter; entry_tree = GTK_TREE_VIEW (glade_xml_get_widget(GPASS_VIEW(window)->xml, "entry-tree")); selection = gtk_tree_view_get_selection(entry_tree); if (gtk_tree_selection_get_selected(selection, &model, &iter)) { gtk_tree_model_get(model, &iter, COLUMN_ENTRY, &window->current, -1); } else { window->current = NULL; } gpass_window_edit_items_sensitive(window); gpass_summary_update(window); } void gpass_entry_tree_on_drag_begin(GtkWidget *widget, GdkDragContext *drag_context, gpointer user_data) { GPassWindow *window; gpass_view_self_from_widget(widget, (gpointer **) &window); gpass_window_before_command(window); } void gpass_entry_tree_on_drag_end(GtkWidget *widget, GdkDragContext *drag_context, gpointer user_data) { GPassWindow *window; gpass_view_self_from_widget(widget, (gpointer **) &window); gpass_window_after_command(window); }