/* $Id$ */ /*- * Copyright (c) 2005 Benedikt Meurer * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 Library General Public * License along with this library; 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 #include static void thunar_uca_chooser_class_init (ThunarUcaChooserClass *klass); static void thunar_uca_chooser_init (ThunarUcaChooser *uca_chooser); static void thunar_uca_chooser_finalize (GObject *object); static gboolean thunar_uca_chooser_key_press_event (GtkWidget *widget, GdkEventKey *event); static void thunar_uca_chooser_response (GtkDialog *dialog, gint response); static void thunar_uca_chooser_exchange (ThunarUcaChooser *uca_chooser, GtkTreeSelection *selection, GtkTreeModel *model, GtkTreeIter *iter_a, GtkTreeIter *iter_b); static void thunar_uca_chooser_open_editor (ThunarUcaChooser *uca_chooser, gboolean edit); static void thunar_uca_chooser_save (ThunarUcaChooser *uca_chooser, ThunarUcaModel *uca_model); static void thunar_uca_chooser_selection_changed (ThunarUcaChooser *uca_chooser, GtkTreeSelection *selection); static void thunar_uca_chooser_add_clicked (ThunarUcaChooser *uca_chooser); static void thunar_uca_chooser_edit_clicked (ThunarUcaChooser *uca_chooser); static void thunar_uca_chooser_delete_clicked (ThunarUcaChooser *uca_chooser); static void thunar_uca_chooser_up_clicked (ThunarUcaChooser *uca_chooser); static void thunar_uca_chooser_down_clicked (ThunarUcaChooser *uca_chooser); struct _ThunarUcaChooserClass { GtkDialogClass __parent__; }; struct _ThunarUcaChooser { GtkDialog __parent__; GtkTooltips *tooltips; GtkWidget *treeview; GtkWidget *add_button; GtkWidget *edit_button; GtkWidget *delete_button; GtkWidget *up_button; GtkWidget *down_button; }; THUNARX_DEFINE_TYPE (ThunarUcaChooser, thunar_uca_chooser, GTK_TYPE_DIALOG); static void thunar_uca_chooser_class_init (ThunarUcaChooserClass *klass) { GtkDialogClass *gtkdialog_class; GtkWidgetClass *gtkwidget_class; GObjectClass *gobject_class; gobject_class = G_OBJECT_CLASS (klass); gobject_class->finalize = thunar_uca_chooser_finalize; gtkwidget_class = GTK_WIDGET_CLASS (klass); gtkwidget_class->key_press_event = thunar_uca_chooser_key_press_event; gtkdialog_class = GTK_DIALOG_CLASS (klass); gtkdialog_class->response = thunar_uca_chooser_response; } static void thunar_uca_chooser_init (ThunarUcaChooser *uca_chooser) { GtkTreeViewColumn *column; GtkTreeSelection *selection; GtkCellRenderer *renderer; ThunarUcaModel *uca_model; GtkWidget *image; GtkWidget *label; GtkWidget *hbox; GtkWidget *swin; GtkWidget *vbox; /* configure the dialog window */ gtk_dialog_add_button (GTK_DIALOG (uca_chooser), GTK_STOCK_HELP, GTK_RESPONSE_HELP); gtk_dialog_add_button (GTK_DIALOG (uca_chooser), GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE); gtk_dialog_set_default_response (GTK_DIALOG (uca_chooser), GTK_RESPONSE_CLOSE); gtk_dialog_set_has_separator (GTK_DIALOG (uca_chooser), FALSE); gtk_window_set_default_size (GTK_WINDOW (uca_chooser), 500, 350); gtk_window_set_destroy_with_parent (GTK_WINDOW (uca_chooser), TRUE); gtk_window_set_title (GTK_WINDOW (uca_chooser), _("Custom Actions")); /* allocate shared tooltips object */ uca_chooser->tooltips = gtk_tooltips_new (); g_object_ref (G_OBJECT (uca_chooser->tooltips)); gtk_object_sink (GTK_OBJECT (uca_chooser->tooltips)); hbox = gtk_hbox_new (FALSE, 3); gtk_container_set_border_width (GTK_CONTAINER (hbox), 6); gtk_box_pack_start (GTK_BOX (GTK_DIALOG (uca_chooser)->vbox), hbox, FALSE, TRUE, 0); gtk_widget_show (hbox); image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_INFO, GTK_ICON_SIZE_DND); gtk_misc_set_alignment (GTK_MISC (image), 0.5f, 0.0f); gtk_box_pack_start (GTK_BOX (hbox), image, FALSE, FALSE, 0); gtk_widget_show (image); label = gtk_label_new (_("You can configure custom actions that will appear in the\n" "file managers context menus for certain kinds of files.")); gtk_misc_set_alignment (GTK_MISC (label), 0.0f, 0.5f); gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, TRUE, 0); gtk_widget_show (label); hbox = gtk_hbox_new (FALSE, 3); gtk_container_set_border_width (GTK_CONTAINER (hbox), 6); gtk_box_pack_start (GTK_BOX (GTK_DIALOG (uca_chooser)->vbox), hbox, TRUE, TRUE, 0); gtk_widget_show (hbox); swin = gtk_scrolled_window_new (NULL, NULL); gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (swin), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (swin), GTK_SHADOW_IN); gtk_box_pack_start (GTK_BOX (hbox), swin, TRUE, TRUE, 0); gtk_widget_show (swin); uca_chooser->treeview = gtk_tree_view_new (); gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (uca_chooser->treeview), FALSE); gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (uca_chooser->treeview), TRUE); gtk_container_add (GTK_CONTAINER (swin), uca_chooser->treeview); g_signal_connect_swapped (G_OBJECT (uca_chooser->treeview), "row-activated", G_CALLBACK (thunar_uca_chooser_edit_clicked), uca_chooser); gtk_widget_show (uca_chooser->treeview); uca_model = thunar_uca_model_get_default (); gtk_tree_view_set_model (GTK_TREE_VIEW (uca_chooser->treeview), GTK_TREE_MODEL (uca_model)); g_object_unref (G_OBJECT (uca_model)); column = gtk_tree_view_column_new (); gtk_tree_view_column_set_expand (column, TRUE); gtk_tree_view_column_set_resizable (column, FALSE); gtk_tree_view_append_column (GTK_TREE_VIEW (uca_chooser->treeview), column); renderer = g_object_new (GTK_TYPE_CELL_RENDERER_PIXBUF, "stock-size", GTK_ICON_SIZE_DND, "xpad", 2, "ypad", 2, NULL); gtk_tree_view_column_pack_start (column, renderer, FALSE); gtk_tree_view_column_set_attributes (column, renderer, "stock-id", THUNAR_UCA_MODEL_COLUMN_STOCK_ID, NULL); renderer = gtk_cell_renderer_text_new (); gtk_tree_view_column_pack_start (column, renderer, TRUE); gtk_tree_view_column_set_attributes (column, renderer, "markup", THUNAR_UCA_MODEL_COLUMN_STOCK_LABEL, NULL); vbox = gtk_vbox_new (FALSE, 3); gtk_box_pack_start (GTK_BOX (hbox), vbox, FALSE, FALSE, 0); gtk_widget_show (vbox); uca_chooser->add_button = gtk_button_new (); gtk_tooltips_set_tip (uca_chooser->tooltips, uca_chooser->add_button, _("Add a new custom action."), NULL); gtk_box_pack_start (GTK_BOX (vbox), uca_chooser->add_button, FALSE, FALSE, 0); g_signal_connect_swapped (G_OBJECT (uca_chooser->add_button), "clicked", G_CALLBACK (thunar_uca_chooser_add_clicked), uca_chooser); gtk_widget_show (uca_chooser->add_button); image = gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_BUTTON); gtk_container_add (GTK_CONTAINER (uca_chooser->add_button), image); gtk_widget_show (image); uca_chooser->edit_button = gtk_button_new (); gtk_tooltips_set_tip (uca_chooser->tooltips, uca_chooser->edit_button, _("Edit the currently selected action."), NULL); gtk_box_pack_start (GTK_BOX (vbox), uca_chooser->edit_button, FALSE, FALSE, 0); g_signal_connect_swapped (G_OBJECT (uca_chooser->edit_button), "clicked", G_CALLBACK (thunar_uca_chooser_edit_clicked), uca_chooser); gtk_widget_show (uca_chooser->edit_button); image = gtk_image_new_from_stock (GTK_STOCK_EDIT, GTK_ICON_SIZE_BUTTON); gtk_container_add (GTK_CONTAINER (uca_chooser->edit_button), image); gtk_widget_show (image); uca_chooser->delete_button = gtk_button_new (); gtk_tooltips_set_tip (uca_chooser->tooltips, uca_chooser->delete_button, _("Delete the currently selected action."), NULL); gtk_box_pack_start (GTK_BOX (vbox), uca_chooser->delete_button, FALSE, FALSE, 0); g_signal_connect_swapped (G_OBJECT (uca_chooser->delete_button), "clicked", G_CALLBACK (thunar_uca_chooser_delete_clicked), uca_chooser); gtk_widget_show (uca_chooser->delete_button); image = gtk_image_new_from_stock (GTK_STOCK_DELETE, GTK_ICON_SIZE_BUTTON); gtk_container_add (GTK_CONTAINER (uca_chooser->delete_button), image); gtk_widget_show (image); uca_chooser->up_button = gtk_button_new (); gtk_tooltips_set_tip (uca_chooser->tooltips, uca_chooser->up_button, _("Move the currently selection action up by one row."), NULL); gtk_box_pack_start (GTK_BOX (vbox), uca_chooser->up_button, FALSE, FALSE, 0); g_signal_connect_swapped (G_OBJECT (uca_chooser->up_button), "clicked", G_CALLBACK (thunar_uca_chooser_up_clicked), uca_chooser); gtk_widget_show (uca_chooser->up_button); image = gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_BUTTON); gtk_container_add (GTK_CONTAINER (uca_chooser->up_button), image); gtk_widget_show (image); uca_chooser->down_button = gtk_button_new (); gtk_tooltips_set_tip (uca_chooser->tooltips, uca_chooser->down_button, _("Move the currently selection action down by one row."), NULL); gtk_box_pack_start (GTK_BOX (vbox), uca_chooser->down_button, FALSE, FALSE, 0); g_signal_connect_swapped (G_OBJECT (uca_chooser->down_button), "clicked", G_CALLBACK (thunar_uca_chooser_down_clicked), uca_chooser); gtk_widget_show (uca_chooser->down_button); image = gtk_image_new_from_stock (GTK_STOCK_GO_DOWN, GTK_ICON_SIZE_BUTTON); gtk_container_add (GTK_CONTAINER (uca_chooser->down_button), image); gtk_widget_show (image); /* configure the tree view selection after the buttons have been created */ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (uca_chooser->treeview)); gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE); g_signal_connect_swapped (G_OBJECT (selection), "changed", G_CALLBACK (thunar_uca_chooser_selection_changed), uca_chooser); thunar_uca_chooser_selection_changed (uca_chooser, selection); } static void thunar_uca_chooser_finalize (GObject *object) { ThunarUcaChooser *uca_chooser = THUNAR_UCA_CHOOSER (object); /* release the tooltips */ g_object_unref (G_OBJECT (uca_chooser->tooltips)); (*G_OBJECT_CLASS (thunar_uca_chooser_parent_class)->finalize) (object); } static gboolean thunar_uca_chooser_key_press_event (GtkWidget *widget, GdkEventKey *event) { /* close chooser window on Esc key press */ if (G_UNLIKELY (event->keyval == GDK_Escape)) { gtk_dialog_response (GTK_DIALOG (widget), GTK_RESPONSE_CLOSE); return TRUE; } return (*GTK_WIDGET_CLASS (thunar_uca_chooser_parent_class)->key_press_event) (widget, event); } static void thunar_uca_chooser_response (GtkDialog *dialog, gint response) { if (response == GTK_RESPONSE_CLOSE) { gtk_widget_destroy (GTK_WIDGET (dialog)); } else if (GTK_DIALOG_CLASS (thunar_uca_chooser_parent_class)->response != NULL) { (*GTK_DIALOG_CLASS (thunar_uca_chooser_parent_class)->response) (dialog, response); } } static void thunar_uca_chooser_selection_changed (ThunarUcaChooser *uca_chooser, GtkTreeSelection *selection) { GtkTreeModel *model; GtkTreePath *path = NULL; GtkTreeIter iter; gboolean selected; g_return_if_fail (THUNAR_UCA_IS_CHOOSER (uca_chooser)); g_return_if_fail (GTK_IS_TREE_SELECTION (selection)); /* check if we have currently selected an item */ selected = gtk_tree_selection_get_selected (selection, &model, &iter); if (G_LIKELY (selected)) { /* determine the path for the selected iter */ path = gtk_tree_model_get_path (model, &iter); } /* change sensitivity of "Edit" and "Delete" appropritately */ gtk_widget_set_sensitive (uca_chooser->edit_button, selected); gtk_widget_set_sensitive (uca_chooser->delete_button, selected); /* change sensitivity of "Move Up" and "Move Down" appropritately */ gtk_widget_set_sensitive (uca_chooser->up_button, selected && gtk_tree_path_get_indices (path)[0] > 0); gtk_widget_set_sensitive (uca_chooser->down_button, selected && gtk_tree_path_get_indices (path)[0] < gtk_tree_model_iter_n_children (model, NULL) - 1); /* release path (if any) */ if (G_LIKELY (path != NULL)) gtk_tree_path_free (path); } static void thunar_uca_chooser_exchange (ThunarUcaChooser *uca_chooser, GtkTreeSelection *selection, GtkTreeModel *model, GtkTreeIter *iter_a, GtkTreeIter *iter_b) { g_return_if_fail (THUNAR_UCA_IS_CHOOSER (uca_chooser)); g_return_if_fail (GTK_IS_TREE_SELECTION (selection)); g_return_if_fail (GTK_IS_TREE_MODEL (model)); g_return_if_fail (iter_a != NULL); g_return_if_fail (iter_b != NULL); /* perform the move */ thunar_uca_model_exchange (THUNAR_UCA_MODEL (model), iter_a, iter_b); /* tell the chooser that the selection may have changed */ thunar_uca_chooser_selection_changed (uca_chooser, selection); /* sync the model to persistent storage */ thunar_uca_chooser_save (uca_chooser, THUNAR_UCA_MODEL (model)); } static void thunar_uca_chooser_open_editor (ThunarUcaChooser *uca_chooser, gboolean edit) { GtkTreeSelection *selection; GtkTreeModel *model; GtkTreeIter iter; GtkWidget *editor; g_return_if_fail (THUNAR_UCA_IS_CHOOSER (uca_chooser)); /* allocate the new editor */ editor = g_object_new (THUNAR_UCA_TYPE_EDITOR, NULL); gtk_window_set_title (GTK_WINDOW (editor), edit ? _("Edit Action") : _("Create Action")); gtk_window_set_transient_for (GTK_WINDOW (editor), GTK_WINDOW (uca_chooser)); /* load the editor with the currently selected item (when editing) */ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (uca_chooser->treeview)); if (gtk_tree_selection_get_selected (selection, &model, &iter) && edit) thunar_uca_editor_load (THUNAR_UCA_EDITOR (editor), THUNAR_UCA_MODEL (model), &iter); /* run the editor */ if (gtk_dialog_run (GTK_DIALOG (editor)) == GTK_RESPONSE_OK) { /* append a new iter (when not editing) */ if (G_UNLIKELY (!edit)) thunar_uca_model_append (THUNAR_UCA_MODEL (model), &iter); /* save the editor values to the model */ thunar_uca_editor_save (THUNAR_UCA_EDITOR (editor), THUNAR_UCA_MODEL (model), &iter); /* hide the editor window */ gtk_widget_hide (editor); /* sync the model to persistent storage */ thunar_uca_chooser_save (uca_chooser, THUNAR_UCA_MODEL (model)); } /* destroy the editor */ gtk_widget_destroy (editor); } static void thunar_uca_chooser_save (ThunarUcaChooser *uca_chooser, ThunarUcaModel *uca_model) { GtkWidget *dialog; GError *error = NULL; g_return_if_fail (THUNAR_UCA_IS_CHOOSER (uca_chooser)); g_return_if_fail (THUNAR_UCA_IS_MODEL (uca_model)); /* sync the model to persistent storage */ if (!thunar_uca_model_save (uca_model, &error)) { dialog = gtk_message_dialog_new (GTK_WINDOW (uca_chooser), GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, _("Failed to save actions to disk.")); gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), _("%s."), error->message); gtk_dialog_run (GTK_DIALOG (dialog)); gtk_widget_destroy (dialog); g_error_free (error); } } static void thunar_uca_chooser_add_clicked (ThunarUcaChooser *uca_chooser) { g_return_if_fail (THUNAR_UCA_IS_CHOOSER (uca_chooser)); thunar_uca_chooser_open_editor (uca_chooser, FALSE); } static void thunar_uca_chooser_edit_clicked (ThunarUcaChooser *uca_chooser) { g_return_if_fail (THUNAR_UCA_IS_CHOOSER (uca_chooser)); thunar_uca_chooser_open_editor (uca_chooser, TRUE); } static void thunar_uca_chooser_delete_clicked (ThunarUcaChooser *uca_chooser) { GtkTreeSelection *selection; GtkTreeModel *model; GtkTreeIter iter; g_return_if_fail (THUNAR_UCA_IS_CHOOSER (uca_chooser)); /* verify that we have an item selected and determine the iter for that item */ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (uca_chooser->treeview)); if (gtk_tree_selection_get_selected (selection, &model, &iter)) { /* remove the row from the model */ thunar_uca_model_remove (THUNAR_UCA_MODEL (model), &iter); /* sync the model to persistent storage */ thunar_uca_chooser_save (uca_chooser, THUNAR_UCA_MODEL (model)); } } static void thunar_uca_chooser_up_clicked (ThunarUcaChooser *uca_chooser) { GtkTreeSelection *selection; GtkTreeModel *model; GtkTreePath *path; GtkTreeIter iter_a; GtkTreeIter iter_b; g_return_if_fail (THUNAR_UCA_IS_CHOOSER (uca_chooser)); /* determine the currently selected item */ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (uca_chooser->treeview)); if (gtk_tree_selection_get_selected (selection, &model, &iter_a)) { /* determine the tree path to the iter */ path = gtk_tree_model_get_path (model, &iter_a); if (gtk_tree_path_prev (path)) { /* determine the iter for the previous item */ gtk_tree_model_get_iter (model, &iter_b, path); /* perform the exchange operation */ thunar_uca_chooser_exchange (uca_chooser, selection, model, &iter_a, &iter_b); } /* release the path */ gtk_tree_path_free (path); } } static void thunar_uca_chooser_down_clicked (ThunarUcaChooser *uca_chooser) { GtkTreeSelection *selection; GtkTreeModel *model; GtkTreeIter iter_a; GtkTreeIter iter_b; g_return_if_fail (THUNAR_UCA_IS_CHOOSER (uca_chooser)); /* determine the currently selected item */ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (uca_chooser->treeview)); if (gtk_tree_selection_get_selected (selection, &model, &iter_a)) { /* determine the iter to the next item */ iter_b = iter_a; if (gtk_tree_model_iter_next (model, &iter_b)) thunar_uca_chooser_exchange (uca_chooser, selection, model, &iter_a, &iter_b); } }