/** * @file gnome-cmd-manage-bookmarks-dialog.cc * @copyright (C) 2001-2006 Marcus Bjurman\n * @copyright (C) 2007-2012 Piotr Eljasiak\n * @copyright (C) 2013-2023 Uwe Scholz\n * * @copyright 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. * * @copyright 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 General Public License for more details. * * @copyright 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include "gnome-cmd-includes.h" #include "gnome-cmd-data.h" #include "gnome-cmd-con-list.h" #include "gnome-cmd-main-win.h" #include "gnome-cmd-manage-bookmarks-dialog.h" #include "gnome-cmd-edit-bookmark-dialog.h" #include "gnome-cmd-treeview.h" #include "gnome-cmd-user-actions.h" #include "gnome-cmd-menu-button.h" #include "gnome-cmd-hintbox.h" #include "eggcellrendererkeys.h" #include "utils.h" using namespace std; static GtkTreeModel *create_and_fill_model (GtkTreePath *¤t_group); static GtkWidget *create_view_and_model (); static void cursor_changed_callback (GtkTreeView *view, GtkWidget *dialog); static void row_activated_callback (GtkTreeView *view, GtkTreePath *path, GtkTreeViewColumn *col, gpointer); static void edit_clicked_callback (GtkButton *button, GtkWidget *view); static void remove_clicked_callback (GtkButton *button, GtkWidget *view); static void up_clicked_callback (GtkButton *button, GtkWidget *view); static void down_clicked_callback (GtkButton *button, GtkWidget *view); static void size_allocate_callback (GtkWidget *widget, GtkAllocation *allocation, gpointer unused); static void response_callback (GtkDialog *dialog, int response_id, GtkTreeView *view); enum { COL_GROUP, COL_NAME, COL_PATH, COL_SHORTCUT, COL_BOOKMARK, NUM_COLUMNS } ; const int RESPONSE_JUMP_TO = 123; GtkWidget *view = NULL; void gnome_cmd_bookmark_dialog_new (const gchar *title, GtkWindow *parent) { GtkWidget *dialog = gtk_dialog_new_with_buttons (title, parent, GtkDialogFlags (GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT), GTK_STOCK_HELP, GTK_RESPONSE_HELP, GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE, GTK_STOCK_JUMP_TO, RESPONSE_JUMP_TO, NULL); GtkWidget *content_area = gtk_dialog_get_content_area(GTK_DIALOG (dialog)); GtkWidget *vbox, *hbox, *scrolled_window, *button; gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_CENTER); gtk_dialog_set_has_separator (GTK_DIALOG (dialog), FALSE); gtk_container_set_border_width (GTK_CONTAINER (dialog), 5); gtk_box_set_spacing (GTK_BOX (content_area), 2); gtk_window_set_resizable (GTK_WINDOW (dialog), TRUE); vbox = gtk_vbox_new (FALSE, 12); gtk_container_set_border_width (GTK_CONTAINER (vbox), 12); gtk_container_add (GTK_CONTAINER (content_area), vbox); hbox = gtk_hbox_new (FALSE, 12); gtk_container_add (GTK_CONTAINER (vbox), hbox); scrolled_window = gtk_scrolled_window_new (NULL, NULL); gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC); gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled_window), GTK_SHADOW_IN); gtk_box_pack_start (GTK_BOX (hbox), scrolled_window, TRUE, TRUE, 0); view = create_view_and_model (); gtk_window_set_default_size (GTK_WINDOW (dialog), gnome_cmd_data.bookmarks_defaults.width, gnome_cmd_data.bookmarks_defaults.height); gtk_widget_set_size_request (view, 400, 250); g_signal_connect (view, "cursor-changed", G_CALLBACK (cursor_changed_callback), dialog); g_signal_connect (view, "row-activated", G_CALLBACK (row_activated_callback), dialog); gtk_container_add (GTK_CONTAINER (scrolled_window), view); vbox = gtk_vbox_new (FALSE, 12); gtk_box_pack_start (GTK_BOX (hbox), vbox, FALSE, FALSE, 0); button = gtk_button_new_from_stock (GTK_STOCK_EDIT); gtk_widget_set_sensitive (button, FALSE); g_signal_connect (button, "clicked", G_CALLBACK (edit_clicked_callback), view); gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0); g_object_set_data (G_OBJECT (dialog), "edit-button", button); button = gtk_button_new_from_stock (GTK_STOCK_REMOVE); gtk_widget_set_sensitive (button, FALSE); g_signal_connect (button, "clicked", G_CALLBACK (remove_clicked_callback), view); gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0); g_object_set_data (G_OBJECT (dialog), "remove-button", button); button = gtk_button_new_from_stock (GTK_STOCK_GO_UP); gtk_widget_set_sensitive (button, FALSE); g_signal_connect (button, "clicked", G_CALLBACK (up_clicked_callback), view); gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0); g_object_set_data (G_OBJECT (dialog), "up-button", button); button = gtk_button_new_from_stock (GTK_STOCK_GO_DOWN); gtk_widget_set_sensitive (button, FALSE); g_signal_connect (button, "clicked", G_CALLBACK (down_clicked_callback), view); gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0); g_object_set_data (G_OBJECT (dialog), "down-button", button); gtk_widget_grab_focus (view); gtk_dialog_set_default_response (GTK_DIALOG (dialog), RESPONSE_JUMP_TO); gtk_dialog_set_response_sensitive (GTK_DIALOG (dialog), RESPONSE_JUMP_TO, FALSE); gtk_widget_show_all (content_area); g_signal_connect (dialog, "size-allocate", G_CALLBACK (size_allocate_callback), NULL); g_signal_connect (dialog, "response", G_CALLBACK (response_callback), view); gtk_dialog_run (GTK_DIALOG (dialog)); gtk_widget_destroy (dialog); view = NULL; } static void fill_tree (GtkTreeStore *store, GtkTreePath *¤t_group) { gtk_tree_store_clear (store); GnomeCmdCon *current_con = main_win->fs(ACTIVE)->get_connection(); GtkTreeIter toplevel; map > keys; for (GnomeCmdUserActions::const_iterator i=gcmd_user_actions.begin(); i!=gcmd_user_actions.end(); ++i) if (!ascii_isupper (*i)) // ignore lowercase keys as they duplicate uppercase ones { const gchar *options = gcmd_user_actions.options(i); if (options && strcmp(gcmd_user_actions.name(i),"bookmarks.goto")==0) { gchar *accelerator = egg_accelerator_get_label(i->first.keyval, (GdkModifierType) i->first.state); keys[options].insert(accelerator); g_free (accelerator); } } for (GList *all_cons = gnome_cmd_con_list_get_all (gnome_cmd_con_list_get ()); all_cons; all_cons = all_cons->next) { GnomeCmdCon *con = (GnomeCmdCon *) all_cons->data; GnomeCmdBookmarkGroup *group = gnome_cmd_con_get_bookmarks (con); if (!group || !group->bookmarks) continue; gtk_tree_store_append (store, &toplevel, NULL); gtk_tree_store_set (store, &toplevel, COL_GROUP, gnome_cmd_con_get_alias (con), -1); if (con==current_con) current_group = gtk_tree_model_get_path (GTK_TREE_MODEL (store), &toplevel); GtkTreeIter child; for (GList *bookmarks = group->bookmarks; bookmarks; bookmarks = bookmarks->next) { gtk_tree_store_append (store, &child, &toplevel); GnomeCmdBookmark *bookmark = (GnomeCmdBookmark *) bookmarks->data; gtk_tree_store_set (store, &child, COL_NAME, bookmark->name, COL_PATH, bookmark->path, COL_SHORTCUT, join(keys[bookmark->name], ", ").c_str(), COL_BOOKMARK, bookmark, -1); } } } static GtkTreeModel *create_and_fill_model (GtkTreePath *¤t_group) { GtkTreeStore *store = gtk_tree_store_new (NUM_COLUMNS, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_POINTER); fill_tree (store, current_group); return GTK_TREE_MODEL (store); } static GtkWidget *create_view_and_model () { GtkWidget *bm_view = gtk_tree_view_new (); g_object_set (bm_view, "rules-hint", TRUE, "enable-search", TRUE, "search-column", COL_NAME, NULL); GtkCellRenderer *renderer = NULL; GtkTreeViewColumn *col = NULL; col = gnome_cmd_treeview_create_new_text_column (GTK_TREE_VIEW (bm_view), renderer, COL_GROUP, _("Group")); gtk_widget_set_tooltip_text (gtk_tree_view_column_get_button (col), _("Bookmark group")); g_object_set (renderer, "weight-set", TRUE, "weight", PANGO_WEIGHT_BOLD, NULL); col = gnome_cmd_treeview_create_new_text_column (GTK_TREE_VIEW (bm_view), renderer, COL_NAME, _("Name")); gtk_widget_set_tooltip_text (gtk_tree_view_column_get_button (col), _("Bookmark name")); col = gnome_cmd_treeview_create_new_text_column (GTK_TREE_VIEW (bm_view), renderer, COL_SHORTCUT, _("Shortcut")); gtk_widget_set_tooltip_text (gtk_tree_view_column_get_button (col), _("Keyboard shortcut for selected bookmark")); g_object_set (renderer, "foreground-set", TRUE, "foreground", "DarkGray", NULL); col = gnome_cmd_treeview_create_new_text_column (GTK_TREE_VIEW (bm_view), renderer, COL_PATH, _("Path")); gtk_widget_set_tooltip_text (gtk_tree_view_column_get_button (col), _("Bookmarked path")); g_object_set (renderer, "foreground-set", TRUE, "foreground", "DarkGray", "ellipsize-set", TRUE, "ellipsize", PANGO_ELLIPSIZE_END, NULL); GtkTreePath *group = NULL; GtkTreeModel *model = create_and_fill_model (group); gtk_tree_view_set_model (GTK_TREE_VIEW (bm_view), model); g_object_unref (model); // destroy model automatically with view GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (bm_view)); gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE); if (group) gtk_tree_view_expand_row (GTK_TREE_VIEW (bm_view), group, TRUE); else group = gtk_tree_path_new_first (); gtk_tree_view_set_cursor (GTK_TREE_VIEW (bm_view), group, NULL, FALSE); gtk_tree_path_free (group); return bm_view; } void gnome_cmd_update_bookmark_dialog () { if (view) { GtkTreePath *group = NULL; fill_tree (GTK_TREE_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW (view))), group); if (group) gtk_tree_view_expand_row (GTK_TREE_VIEW (view), group, TRUE); } } static void cursor_changed_callback (GtkTreeView *bm_view, GtkWidget *dialog) { GtkTreeModel *bm_model = gtk_tree_view_get_model (bm_view); GtkTreeIter iter; if (gtk_tree_selection_get_selected (gtk_tree_view_get_selection (bm_view), NULL, &iter)) { GnomeCmdBookmark *bookmark = NULL; gtk_tree_model_get (bm_model, &iter, COL_BOOKMARK, &bookmark, -1); gtk_dialog_set_response_sensitive (GTK_DIALOG (dialog), RESPONSE_JUMP_TO, bookmark!=NULL); gtk_widget_set_sensitive ((GtkWidget *) g_object_get_data (G_OBJECT (dialog), "edit-button"), bookmark!=NULL); gtk_widget_set_sensitive ((GtkWidget *) g_object_get_data (G_OBJECT (dialog), "remove-button"), bookmark!=NULL); gtk_widget_set_sensitive ((GtkWidget *) g_object_get_data (G_OBJECT (dialog), "up-button"), bookmark!=NULL && bookmark->group->bookmarks->data!=bookmark); gtk_widget_set_sensitive ((GtkWidget *) g_object_get_data (G_OBJECT (dialog), "down-button"), bookmark!=NULL && gtk_tree_model_iter_next (bm_model, &iter)); } } static void row_activated_callback (GtkTreeView *bm_view, GtkTreePath *path, GtkTreeViewColumn *col, gpointer) { gtk_dialog_response ((GtkDialog *) gtk_widget_get_ancestor (GTK_WIDGET (bm_view), GTK_TYPE_DIALOG), RESPONSE_JUMP_TO); } static void edit_clicked_callback (GtkButton *button, GtkWidget *bm_view) { GtkWidget *dialog = gtk_widget_get_ancestor (bm_view, GTK_TYPE_DIALOG); g_return_if_fail (dialog!=NULL); GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (bm_view)); GtkTreeIter iter; if (gtk_tree_selection_get_selected (selection, NULL, &iter)) { GnomeCmdBookmark *bookmark = NULL; GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (bm_view)); gtk_tree_model_get (model, &iter, COL_BOOKMARK, &bookmark, -1); if (gnome_cmd_edit_bookmark_dialog (GTK_WINDOW (dialog), _("Edit Bookmark"), bookmark->name, bookmark->path)) gtk_tree_store_set (GTK_TREE_STORE (model), &iter, COL_NAME, bookmark->name, COL_PATH, bookmark->path, -1); } } static void remove_clicked_callback (GtkButton *button, GtkWidget *bm_view) { GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (bm_view)); GtkTreeIter iter; if (gtk_tree_selection_get_selected (selection, NULL, &iter)) { GnomeCmdBookmark *bookmark = NULL; GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (bm_view)); gtk_tree_model_get (model, &iter, COL_BOOKMARK, &bookmark, -1); gtk_tree_store_remove (GTK_TREE_STORE (model), &iter); if (bookmark) { bookmark->group->bookmarks = g_list_remove (bookmark->group->bookmarks, bookmark); g_free (bookmark->name); g_free (bookmark->path); g_free (bookmark); main_win->update_bookmarks (); gnome_cmd_data.save_bookmarks (); } } } static void up_clicked_callback (GtkButton *button, GtkWidget *bm_view) { GtkWidget *dialog = gtk_widget_get_ancestor (bm_view, GTK_TYPE_DIALOG); GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (bm_view)); GtkTreeIter iter; if (!gtk_tree_selection_get_selected (selection, NULL, &iter)) return; GnomeCmdBookmark *bookmark = NULL; GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (bm_view)); gtk_tree_model_get (model, &iter, COL_BOOKMARK, &bookmark, -1); if (!bookmark) return; GList *elem = g_list_find (bookmark->group->bookmarks, bookmark); if (!elem) return; bookmark->group->bookmarks = g_list_insert_before (bookmark->group->bookmarks, g_list_previous (elem), bookmark); bookmark->group->bookmarks = g_list_remove_link (bookmark->group->bookmarks, elem); GtkTreePath *path = gtk_tree_model_get_path (model, &iter); GtkTreeIter prev; gtk_tree_path_prev (path); gtk_tree_model_get_iter (model, &prev, path); gtk_tree_path_free (path); gtk_tree_store_swap (GTK_TREE_STORE (model), &prev, &iter); gtk_widget_set_sensitive ((GtkWidget *) g_object_get_data (G_OBJECT (dialog), "up-button"), bookmark->group->bookmarks->data!=bookmark); gtk_widget_set_sensitive ((GtkWidget *) g_object_get_data (G_OBJECT (dialog), "down-button"), gtk_tree_model_iter_next (model, &iter)); } static void down_clicked_callback (GtkButton *button, GtkWidget *bm_view) { GtkWidget *dialog = gtk_widget_get_ancestor (bm_view, GTK_TYPE_DIALOG); GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (bm_view)); GtkTreeIter iter; if (!gtk_tree_selection_get_selected (selection, NULL, &iter)) return; GnomeCmdBookmark *bookmark = NULL; GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (bm_view)); gtk_tree_model_get (model, &iter, COL_BOOKMARK, &bookmark, -1); if (!bookmark) return; GList *elem = g_list_find (bookmark->group->bookmarks, bookmark); if (!elem) return; elem = g_list_insert (elem, bookmark, 2); bookmark->group->bookmarks = g_list_remove_link (bookmark->group->bookmarks, elem); GtkTreeIter next = iter; gtk_tree_model_iter_next (model, &next); gtk_tree_store_swap (GTK_TREE_STORE (model), &iter, &next); gtk_widget_set_sensitive ((GtkWidget *) g_object_get_data (G_OBJECT (dialog), "up-button"), bookmark!=NULL && bookmark->group->bookmarks->data!=bookmark); gtk_widget_set_sensitive ((GtkWidget *) g_object_get_data (G_OBJECT (dialog), "down-button"), bookmark!=NULL && gtk_tree_model_iter_next (model, &iter)); } static void size_allocate_callback (GtkWidget *widget, GtkAllocation *allocation, gpointer unused) { gnome_cmd_data.bookmarks_defaults.width = allocation->width; gnome_cmd_data.bookmarks_defaults.height = allocation->height; } static void response_callback (GtkDialog *dialog, int response_id, GtkTreeView *bm_view) { switch (response_id) { case GTK_RESPONSE_HELP: gnome_cmd_help_display ("gnome-commander.xml", "gnome-commander-bookmarks"); g_signal_stop_emission_by_name (dialog, "response"); break; case RESPONSE_JUMP_TO: { GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (bm_view)); GtkTreeIter iter; if (gtk_tree_selection_get_selected (gtk_tree_view_get_selection (GTK_TREE_VIEW (bm_view)), NULL, &iter)) { GnomeCmdBookmark *bookmark = NULL; gtk_tree_model_get (model, &iter, COL_BOOKMARK, &bookmark, -1); if (bookmark) gnome_cmd_bookmark_goto (bookmark); else g_signal_stop_emission_by_name (dialog, "response"); } else g_signal_stop_emission_by_name (dialog, "response"); } case GTK_RESPONSE_CLOSE: break; case GTK_RESPONSE_NONE: case GTK_RESPONSE_DELETE_EVENT: case GTK_RESPONSE_CANCEL: break; default : g_assert_not_reached (); } } void gnome_cmd_bookmark_goto (GnomeCmdBookmark *bookmark) { g_return_if_fail (bookmark->group->con != NULL); GnomeCmdFileSelector *fs = main_win->fs(ACTIVE); g_return_if_fail (GNOME_CMD_IS_FILE_SELECTOR (fs)); GnomeCmdCon *con = bookmark->group->con; if (fs->get_connection() == con) { if (fs->file_list()->locked) fs->new_tab(gnome_cmd_dir_new (con, gnome_cmd_con_create_path (con, bookmark->path))); else fs->goto_directory(bookmark->path); } else { if (con->state == GnomeCmdCon::STATE_OPEN) { GnomeCmdDir *dir = gnome_cmd_dir_new (con, gnome_cmd_con_create_path (con, bookmark->path)); if (fs->file_list()->locked) fs->new_tab(dir); else fs->set_connection(con, dir); } else { delete con->base_path; con->base_path = gnome_cmd_con_create_path (con, bookmark->path); if (fs->file_list()->locked) fs->new_tab(gnome_cmd_con_get_default_dir (con)); else fs->set_connection(con); } } } void gnome_cmd_bookmark_add_current (GnomeCmdDir *dir) { gchar *path = gnome_cmd_dir_is_local (dir) ? GNOME_CMD_FILE (dir)->get_real_path () : GNOME_CMD_FILE (dir)->GetPathStringThroughParent(); if (!g_utf8_validate (path, -1, NULL)) { gnome_cmd_show_message (NULL, _("To bookmark a directory the whole search path to the directory must be in valid UTF-8 encoding")); g_free (path); return; } gchar *name = g_path_get_basename (path); if (gnome_cmd_edit_bookmark_dialog (NULL, _("New Bookmark"), name, path)) { GnomeCmdCon *con = gnome_cmd_dir_is_local (dir) ? get_home_con () : gnome_cmd_dir_get_connection (dir); GnomeCmdBookmarkGroup *group = gnome_cmd_con_get_bookmarks (con); GnomeCmdBookmark *bookmark = g_new0 (GnomeCmdBookmark, 1); bookmark->name = name; bookmark->path = path; bookmark->group = group; group->bookmarks = g_list_append (group->bookmarks, bookmark); main_win->update_bookmarks(); gnome_cmd_data.save_bookmarks (); } else { g_free (name); g_free (path); } }