/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ /* * Pix * * Copyright (C) 2009 Free Software Foundation, Inc. * * 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include "dlg-personalize-scripts.h" #include "gth-script.h" #include "gth-script-editor-dialog.h" #include "gth-script-file.h" #include "shortcuts.h" #define GET_WIDGET(name) _gtk_builder_get_widget (data->builder, (name)) #define ORDER_CHANGED_DELAY 250 enum { COLUMN_SCRIPT, COLUMN_NAME, COLUMN_SHORTCUT, COLUMN_VISIBLE, NUM_COLUMNS }; typedef struct { GthBrowser *browser; GtkBuilder *builder; GtkWidget *dialog; GtkWidget *list_view; GtkListStore *list_store; gulong scripts_changed_id; guint list_changed_id; } DialogData; static void destroy_cb (GtkWidget *widget, DialogData *data) { if (data->list_changed_id != 0) g_source_remove (data->list_changed_id); data->list_changed_id = 0; gth_browser_set_dialog (data->browser, "personalize_scripts", NULL); g_signal_handler_disconnect (gth_script_file_get (), data->scripts_changed_id); g_object_unref (data->builder); g_free (data); } static gboolean list_view_row_order_changed_cb (gpointer user_data) { DialogData *data = user_data; GtkTreeModel *model = GTK_TREE_MODEL (data->list_store); GtkTreeIter iter; if (data->list_changed_id != 0) g_source_remove (data->list_changed_id); data->list_changed_id = 0; if (gtk_tree_model_get_iter_first (model, &iter)) { GthScriptFile *script_file; script_file = gth_script_file_get (); gth_script_file_clear (script_file); do { GthScript *script; gtk_tree_model_get (model, &iter, COLUMN_SCRIPT, &script, -1); gth_script_file_add (script_file, script); g_object_unref (script); } while (gtk_tree_model_iter_next (model, &iter)); gth_script_file_save (script_file, NULL); } return FALSE; } static void row_deleted_cb (GtkTreeModel *tree_model, GtkTreePath *path, gpointer user_data) { DialogData *data = user_data; if (data->list_changed_id != 0) g_source_remove (data->list_changed_id); data->list_changed_id = g_timeout_add (ORDER_CHANGED_DELAY, list_view_row_order_changed_cb, data); } static void row_inserted_cb (GtkTreeModel *tree_model, GtkTreePath *path, GtkTreeIter *iter, gpointer user_data) { DialogData *data = user_data; if (data->list_changed_id != 0) g_source_remove (data->list_changed_id); data->list_changed_id = g_timeout_add (ORDER_CHANGED_DELAY, list_view_row_order_changed_cb, data); } static char * get_shortcut_label (DialogData *data, GthScript *script) { GthShortcut *shortcut; shortcut = gth_window_get_shortcut (GTH_WINDOW (data->browser), gth_script_get_detailed_action (script)); return (shortcut != NULL) ? shortcut->label : ""; } static void set_script_list (DialogData *data, GList *script_list) { GList *scan; g_signal_handlers_block_by_func (data->list_store, row_inserted_cb, data); for (scan = script_list; scan; scan = scan->next) { GthScript *script = scan->data; GtkTreeIter iter; gtk_list_store_append (data->list_store, &iter); gtk_list_store_set (data->list_store, &iter, COLUMN_SCRIPT, script, COLUMN_NAME, gth_script_get_display_name (script), COLUMN_SHORTCUT, get_shortcut_label (data, script), COLUMN_VISIBLE, gth_script_is_visible (script), -1); } g_signal_handlers_unblock_by_func (data->list_store, row_inserted_cb, data); } static void update_script_list (DialogData *data) { GthScriptFile *script_file; GList *script_list; g_signal_handlers_block_by_func (data->list_store, row_deleted_cb, data); gtk_list_store_clear (data->list_store); g_signal_handlers_unblock_by_func (data->list_store, row_deleted_cb, data); script_file = gth_script_file_get (); script_list = gth_script_file_get_scripts (script_file); set_script_list (data, script_list); _g_object_list_unref (script_list); } static void scripts_changed_cb (GthScriptFile *script_file, DialogData *data) { update_script_list (data); } static void cell_renderer_toggle_toggled_cb (GtkCellRendererToggle *cell_renderer, char *path, gpointer user_data) { DialogData *data = user_data; GtkTreePath *tpath; GtkTreeIter iter; gboolean visible; tpath = gtk_tree_path_new_from_string (path); if (tpath == NULL) return; if (gtk_tree_model_get_iter (GTK_TREE_MODEL (data->list_store), &iter, tpath)) { GthScript *script; GthScriptFile *script_file; gtk_tree_model_get (GTK_TREE_MODEL (data->list_store), &iter, COLUMN_SCRIPT, &script, COLUMN_VISIBLE, &visible, -1); visible = ! visible; g_object_set (script, "visible", visible, NULL); script_file = gth_script_file_get (); g_signal_handlers_block_by_func (script_file, scripts_changed_cb, data); gth_script_file_add (script_file, script); gth_script_file_save (script_file, NULL); g_signal_handlers_unblock_by_func (script_file, scripts_changed_cb, data); gtk_list_store_set (data->list_store, &iter, COLUMN_VISIBLE, visible, -1); g_object_unref (script); } gtk_tree_path_free (tpath); } static void add_columns (GtkTreeView *treeview, DialogData *data) { GtkCellRenderer *renderer; GtkTreeViewColumn *column; /* the name column. */ column = gtk_tree_view_column_new (); gtk_tree_view_column_set_title (column, _("Script")); renderer = gtk_cell_renderer_text_new (); gtk_tree_view_column_pack_start (column, renderer, TRUE); gtk_tree_view_column_set_attributes (column, renderer, "text", COLUMN_NAME, NULL); gtk_tree_view_column_set_expand (column, TRUE); gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column); /* the shortcut column */ column = gtk_tree_view_column_new (); gtk_tree_view_column_set_title (column, _("Shortcut")); renderer = gtk_cell_renderer_text_new (); g_object_set (renderer, "xalign", 0.5, NULL); gtk_tree_view_column_pack_start (column, renderer, TRUE); gtk_tree_view_column_set_attributes (column, renderer, "text", COLUMN_SHORTCUT, NULL); gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column); /* the checkbox column */ column = gtk_tree_view_column_new (); gtk_tree_view_column_set_title (column, _("Show")); renderer = gtk_cell_renderer_toggle_new (); g_signal_connect (renderer, "toggled", G_CALLBACK (cell_renderer_toggle_toggled_cb), data); gtk_tree_view_column_pack_start (column, renderer, TRUE); gtk_tree_view_column_set_attributes (column, renderer, "active", COLUMN_VISIBLE, NULL); gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column); } static gboolean get_iter_for_shortcut (DialogData *data, GthShortcut *shortcut, GtkTreeIter *iter) { GtkTreeModel *model = GTK_TREE_MODEL (data->list_store); gboolean found = FALSE; if (! gtk_tree_model_get_iter_first (model, iter)) return FALSE; do { GthScript *script; gtk_tree_model_get (model, iter, COLUMN_SCRIPT, &script, -1); found = g_strcmp0 (shortcut->detailed_action, gth_script_get_detailed_action (script)) == 0; g_object_unref (script); } while (! found && gtk_tree_model_iter_next (model, iter)); return found; } static gboolean get_iter_script (DialogData *data, GthScript *script, GtkTreeIter *iter) { GtkTreeModel *model = GTK_TREE_MODEL (data->list_store); gboolean found = FALSE; const char *script_id; script_id = gth_script_get_id (script); if (! gtk_tree_model_get_iter_first (model, iter)) return FALSE; do { GthScript *list_script; gtk_tree_model_get (model, iter, COLUMN_SCRIPT, &list_script, -1); found = g_strcmp0 (script_id, gth_script_get_id (list_script)) == 0; g_object_unref (list_script); } while (! found && gtk_tree_model_iter_next (model, iter)); return found; } static void script_editor_dialog__response_cb (GtkDialog *dialog, int response, gpointer user_data) { DialogData *data = user_data; GthScript *script; GError *error = NULL; GPtrArray *shortcuts_v; GthScriptFile *script_file; gboolean new_script; GthShortcut *shortcut; GtkTreeIter iter; gboolean change_list; if (response != GTK_RESPONSE_OK) { gtk_widget_destroy (GTK_WIDGET (dialog)); return; } script = gth_script_editor_dialog_get_script (GTH_SCRIPT_EDITOR_DIALOG (dialog), &error); if (script == NULL) { _gtk_error_dialog_from_gerror_show (GTK_WINDOW (dialog), _("Could not save the script"), error); g_clear_error (&error); return; } /* update the shortcuts */ shortcuts_v = _g_ptr_array_dup (gth_window_get_shortcuts (GTH_WINDOW (data->browser)), (GCopyFunc) gth_shortcut_dup, (GDestroyNotify) gth_shortcut_free); /* If another shortcut has the same accelerator, reset the accelerator * for that shortcut. */ shortcut = gth_shortcut_array_find_by_accel (shortcuts_v, GTH_SHORTCUT_CONTEXT_BROWSER_VIEWER, GTH_SHORTCUT_VIEWER_CONTEXT_ANY, gth_script_get_accelerator (script)); if (shortcut != NULL) { if (g_strcmp0 (shortcut->detailed_action, gth_script_get_detailed_action (script)) != 0) { if (get_iter_for_shortcut (data, shortcut, &iter)) gtk_list_store_set (data->list_store, &iter, COLUMN_SHORTCUT, "", -1); gth_shortcut_set_key (shortcut, 0, 0); } } /* update the script shortcut */ shortcut = gth_shortcut_array_find_by_action (shortcuts_v, gth_script_get_detailed_action (script)); if (shortcut != NULL) g_ptr_array_remove (shortcuts_v, shortcut); shortcut = gth_script_create_shortcut (script); g_ptr_array_add (shortcuts_v, shortcut); /* save the script */ script_file = gth_script_file_get (); new_script = ! gth_script_file_has_script (script_file, script); g_signal_handlers_block_by_func (script_file, scripts_changed_cb, data); gth_script_file_add (script_file, script); gth_script_file_save (script_file, NULL); g_signal_handlers_unblock_by_func (script_file, scripts_changed_cb, data); gth_main_shortcuts_changed (shortcuts_v); /* update the script list */ if (new_script) { g_signal_handlers_block_by_func (data->list_store, row_inserted_cb, data); gtk_list_store_append (data->list_store, &iter); g_signal_handlers_unblock_by_func (data->list_store, row_inserted_cb, data); change_list = TRUE; } else change_list = get_iter_script (data, script, &iter); if (change_list) gtk_list_store_set (data->list_store, &iter, COLUMN_SCRIPT, script, COLUMN_NAME, gth_script_get_display_name (script), COLUMN_SHORTCUT, shortcut->label, COLUMN_VISIBLE, gth_script_is_visible (script), -1); gtk_widget_destroy (GTK_WIDGET (dialog)); g_ptr_array_unref (shortcuts_v); g_object_unref (script); } static void new_script_cb (GtkButton *button, DialogData *data) { GtkWidget *dialog; dialog = gth_script_editor_dialog_new (_("New Command"), GTH_WINDOW (data->browser), GTK_WINDOW (data->dialog)); g_signal_connect (dialog, "response", G_CALLBACK (script_editor_dialog__response_cb), data); gtk_window_set_modal (GTK_WINDOW (dialog), TRUE); gtk_window_present (GTK_WINDOW (dialog)); } static void edit_script_cb (GtkButton *button, DialogData *data) { GtkTreeSelection *selection; GtkTreeModel *model = GTK_TREE_MODEL (data->list_store); GtkTreeIter iter; GthScript *script; GtkWidget *dialog; selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (data->list_view)); if (! gtk_tree_selection_get_selected (selection, &model, &iter)) return; gtk_tree_model_get (model, &iter, COLUMN_SCRIPT, &script, -1); if (script == NULL) return; dialog = gth_script_editor_dialog_new (_("Edit Command"), GTH_WINDOW (data->browser), GTK_WINDOW (data->dialog)); gth_script_editor_dialog_set_script (GTH_SCRIPT_EDITOR_DIALOG (dialog), script); g_signal_connect (dialog, "response", G_CALLBACK (script_editor_dialog__response_cb), data); gtk_window_set_modal (GTK_WINDOW (dialog), TRUE); gtk_window_present (GTK_WINDOW (dialog)); g_object_unref (script); } static void delete_script_cb (GtkButton *button, DialogData *data) { GtkWidget *d; int result; GtkTreeSelection *selection; GtkTreeModel *model = GTK_TREE_MODEL (data->list_store); GtkTreeIter iter; GthScript *script; GPtrArray *shortcuts_v; GthShortcut *shortcut; GthScriptFile *script_file; d = _gtk_message_dialog_new (GTK_WINDOW (data->dialog), GTK_DIALOG_MODAL, _GTK_ICON_NAME_DIALOG_QUESTION, _("Are you sure you want to delete the selected command?"), NULL, _GTK_LABEL_CANCEL, GTK_RESPONSE_CANCEL, _GTK_LABEL_DELETE, GTK_RESPONSE_OK, NULL); result = gtk_dialog_run (GTK_DIALOG (d)); gtk_widget_destroy (d); if (result != GTK_RESPONSE_OK) return; selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (data->list_view)); if (! gtk_tree_selection_get_selected (selection, &model, &iter)) return; gtk_tree_model_get (model, &iter, COLUMN_SCRIPT, &script, -1); if (script == NULL) return; /* update the shortcuts */ shortcuts_v = _g_ptr_array_dup (gth_window_get_shortcuts (GTH_WINDOW (data->browser)), (GCopyFunc) gth_shortcut_dup, (GDestroyNotify) gth_shortcut_free); shortcut = gth_shortcut_array_find_by_action (shortcuts_v, gth_script_get_detailed_action (script)); if (shortcut != NULL) g_ptr_array_remove (shortcuts_v, shortcut); /* update the script file */ script_file = gth_script_file_get (); g_signal_handlers_block_by_func (script_file, scripts_changed_cb, data); gth_script_file_remove (script_file, script); gth_script_file_save (script_file, NULL); g_signal_handlers_unblock_by_func (script_file, scripts_changed_cb, data); gth_main_shortcuts_changed (shortcuts_v); /* update the script list */ g_signal_handlers_block_by_func (data->list_store, row_deleted_cb, data); gtk_list_store_remove (data->list_store, &iter); g_signal_handlers_unblock_by_func (data->list_store, row_deleted_cb, data); g_object_unref (script); } static void update_sensitivity (DialogData *data) { GtkTreeModel *model; GtkTreeSelection *selection; GtkTreeIter iter; gboolean script_selected; model = GTK_TREE_MODEL (data->list_store); selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (data->list_view)); script_selected = gtk_tree_selection_get_selected (selection, &model, &iter); gtk_widget_set_sensitive (GET_WIDGET ("edit_button"), script_selected); gtk_widget_set_sensitive (GET_WIDGET ("delete_button"), script_selected); } static void list_view_selection_changed_cb (GtkTreeSelection *treeselection, gpointer user_data) { update_sensitivity ((DialogData *) user_data); } static void list_view_row_activated_cb (GtkTreeView *tree_view, GtkTreePath *path, GtkTreeViewColumn *column, gpointer user_data) { edit_script_cb (NULL, user_data); } void dlg_personalize_scripts (GthBrowser *browser) { DialogData *data; if (gth_browser_get_dialog (browser, "personalize_scripts") != NULL) { gtk_window_present (GTK_WINDOW (gth_browser_get_dialog (browser, "personalize_scripts"))); return; } data = g_new0 (DialogData, 1); data->browser = browser; data->builder = gtk_builder_new_from_resource ("/org/x/Pix/list_tools/data/ui/personalize-scripts.ui"); /* Get the widgets. */ data->dialog = g_object_new (GTK_TYPE_DIALOG, "title", _("Commands"), "transient-for", GTK_WINDOW (browser), "modal", FALSE, "destroy-with-parent", FALSE, "use-header-bar", _gtk_settings_get_dialogs_use_header (), NULL); gtk_container_add (GTK_CONTAINER (gtk_dialog_get_content_area (GTK_DIALOG (data->dialog))), _gtk_builder_get_widget (data->builder, "dialog_content")); gtk_dialog_add_buttons (GTK_DIALOG (data->dialog), _GTK_LABEL_OK, GTK_RESPONSE_CLOSE, NULL); gth_browser_set_dialog (browser, "personalize_scripts", data->dialog); g_object_set_data (G_OBJECT (data->dialog), "dialog_data", data); /**/ data->list_store = gtk_list_store_new (NUM_COLUMNS, G_TYPE_OBJECT, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_BOOLEAN); data->list_view = gtk_tree_view_new_with_model (GTK_TREE_MODEL (data->list_store)); g_object_unref (data->list_store); gtk_tree_view_set_reorderable (GTK_TREE_VIEW (data->list_view), TRUE); gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (data->list_view), TRUE); add_columns (GTK_TREE_VIEW (data->list_view), data); gtk_widget_show (data->list_view); gtk_container_add (GTK_CONTAINER (GET_WIDGET ("scripts_scrolledwindow")), data->list_view); gtk_label_set_mnemonic_widget (GTK_LABEL (GET_WIDGET ("scripts_label")), data->list_view); gtk_label_set_use_underline (GTK_LABEL (GET_WIDGET ("scripts_label")), TRUE); update_script_list (data); update_sensitivity (data); /* Set the signals handlers. */ g_signal_connect (G_OBJECT (data->dialog), "destroy", G_CALLBACK (destroy_cb), data); g_signal_connect_swapped (gtk_dialog_get_widget_for_response (GTK_DIALOG (data->dialog), GTK_RESPONSE_CLOSE), "clicked", G_CALLBACK (gtk_widget_destroy), G_OBJECT (data->dialog)); g_signal_connect (G_OBJECT (GET_WIDGET ("new_button")), "clicked", G_CALLBACK (new_script_cb), data); g_signal_connect (G_OBJECT (GET_WIDGET ("edit_button")), "clicked", G_CALLBACK (edit_script_cb), data); g_signal_connect (G_OBJECT (GET_WIDGET ("delete_button")), "clicked", G_CALLBACK (delete_script_cb), data); g_signal_connect (gtk_tree_view_get_selection (GTK_TREE_VIEW (data->list_view)), "changed", G_CALLBACK (list_view_selection_changed_cb), data); g_signal_connect (GTK_TREE_VIEW (data->list_view), "row-activated", G_CALLBACK (list_view_row_activated_cb), data); g_signal_connect (data->list_store, "row-deleted", G_CALLBACK (row_deleted_cb), data); g_signal_connect (data->list_store, "row-inserted", G_CALLBACK (row_inserted_cb), data); data->scripts_changed_id = g_signal_connect (gth_script_file_get (), "changed", G_CALLBACK (scripts_changed_cb), data); /* run dialog. */ gtk_widget_show (data->dialog); }