Tree of tag thunar-4.20.7 (commit df0e054) from https://gitlab.xfce.org/xfce/thunar. Upstream history is not included here to keep the repository small.
1982 lines
75 KiB
C
1982 lines
75 KiB
C
/* vi:set et ai sw=2 sts=2 ts=2: */
|
|
/*-
|
|
* Copyright (c) 2005-2007 Benedikt Meurer <benny@xfce.org>
|
|
*
|
|
* 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, write to the Free Software Foundation, Inc., 59 Temple
|
|
* Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#ifdef HAVE_MEMORY_H
|
|
#include <memory.h>
|
|
#endif
|
|
#ifdef HAVE_STRING_H
|
|
#include <string.h>
|
|
#endif
|
|
|
|
#include "thunar/thunar-abstract-dialog.h"
|
|
#include "thunar/thunar-action-manager.h"
|
|
#include "thunar/thunar-application.h"
|
|
#include "thunar/thunar-dialogs.h"
|
|
#include "thunar/thunar-gtk-extensions.h"
|
|
#include "thunar/thunar-icon-factory.h"
|
|
#include "thunar/thunar-icon-renderer.h"
|
|
#include "thunar/thunar-menu.h"
|
|
#include "thunar/thunar-preferences.h"
|
|
#include "thunar/thunar-private.h"
|
|
#include "thunar/thunar-properties-dialog.h"
|
|
#include "thunar/thunar-renamer-dialog.h"
|
|
#include "thunar/thunar-renamer-model.h"
|
|
#include "thunar/thunar-renamer-progress.h"
|
|
#include "thunar/thunar-util.h"
|
|
|
|
#include <libxfce4ui/libxfce4ui.h>
|
|
|
|
|
|
|
|
/* Property identifiers */
|
|
enum
|
|
{
|
|
PROP_0,
|
|
PROP_CURRENT_DIRECTORY,
|
|
PROP_SELECTED_FILES,
|
|
PROP_STANDALONE,
|
|
};
|
|
|
|
/* Identifiers for DnD target types */
|
|
enum
|
|
{
|
|
TARGET_GTK_TREE_MODEL_ROW,
|
|
TARGET_TEXT_URI_LIST,
|
|
};
|
|
|
|
typedef enum
|
|
{
|
|
THUNAR_RENAMER_ACTION_ADD_FILES,
|
|
THUNAR_RENAMER_ACTION_REMOVE_FILES,
|
|
THUNAR_RENAMER_ACTION_CLEAR,
|
|
THUNAR_RENAMER_ACTION_ABOUT,
|
|
|
|
} ThunarRenamerAction;
|
|
|
|
|
|
|
|
static void
|
|
thunar_renamer_dialog_dispose (GObject *object);
|
|
static void
|
|
thunar_renamer_dialog_finalize (GObject *object);
|
|
static void
|
|
thunar_renamer_dialog_get_property (GObject *object,
|
|
guint prop_id,
|
|
GValue *value,
|
|
GParamSpec *pspec);
|
|
static void
|
|
thunar_renamer_dialog_set_property (GObject *object,
|
|
guint prop_id,
|
|
const GValue *value,
|
|
GParamSpec *pspec);
|
|
static void
|
|
thunar_renamer_dialog_realize (GtkWidget *widget);
|
|
static void
|
|
thunar_renamer_dialog_unrealize (GtkWidget *widget);
|
|
static void
|
|
thunar_renamer_dialog_response (GtkDialog *dialog,
|
|
gint response);
|
|
static void
|
|
thunar_renamer_dialog_context_menu (ThunarRenamerDialog *renamer_dialog);
|
|
static void
|
|
thunar_renamer_dialog_help (ThunarRenamerDialog *renamer_dialog);
|
|
static void
|
|
thunar_renamer_dialog_save (ThunarRenamerDialog *renamer_dialog);
|
|
static gboolean
|
|
thunar_renamer_dialog_action_add_files (ThunarRenamerDialog *renamer_dialog);
|
|
static gboolean
|
|
thunar_renamer_dialog_action_remove_files (ThunarRenamerDialog *renamer_dialog);
|
|
static gboolean
|
|
thunar_renamer_dialog_action_clear (ThunarRenamerDialog *renamer_dialog);
|
|
static gboolean
|
|
thunar_renamer_dialog_action_about (ThunarRenamerDialog *renamer_dialog);
|
|
static void
|
|
thunar_renamer_dialog_name_column_clicked (GtkTreeViewColumn *column,
|
|
ThunarRenamerDialog *renamer_dialog);
|
|
static gboolean
|
|
thunar_renamer_dialog_button_press_event (GtkWidget *tree_view,
|
|
GdkEventButton *event,
|
|
ThunarRenamerDialog *renamer_dialog);
|
|
static void
|
|
thunar_renamer_dialog_drag_data_received (GtkWidget *tree_view,
|
|
GdkDragContext *context,
|
|
gint x,
|
|
gint y,
|
|
GtkSelectionData *selection_data,
|
|
guint info,
|
|
guint timestamp,
|
|
ThunarRenamerDialog *renamer_dialog);
|
|
static void
|
|
thunar_renamer_dialog_drag_leave (GtkWidget *tree_view,
|
|
GdkDragContext *context,
|
|
guint timestamp,
|
|
ThunarRenamerDialog *renamer_dialog);
|
|
static gboolean
|
|
thunar_renamer_dialog_drag_motion (GtkWidget *tree_view,
|
|
GdkDragContext *context,
|
|
gint x,
|
|
gint y,
|
|
guint timestamp,
|
|
ThunarRenamerDialog *renamer_dialog);
|
|
static gboolean
|
|
thunar_renamer_dialog_drag_drop (GtkWidget *tree_view,
|
|
GdkDragContext *context,
|
|
gint x,
|
|
gint y,
|
|
guint timestamp,
|
|
ThunarRenamerDialog *renamer_dialog);
|
|
static void
|
|
thunar_renamer_dialog_notify_page (GtkNotebook *notebook,
|
|
GParamSpec *pspec,
|
|
ThunarRenamerDialog *renamer_dialog);
|
|
static gboolean
|
|
thunar_renamer_dialog_popup_menu (GtkWidget *tree_view,
|
|
ThunarRenamerDialog *renamer_dialog);
|
|
static void
|
|
thunar_renamer_dialog_row_activated (GtkTreeView *tree_view,
|
|
GtkTreePath *path,
|
|
GtkTreeViewColumn *column,
|
|
ThunarRenamerDialog *renamer_dialog);
|
|
static void
|
|
thunar_renamer_dialog_selection_changed (GtkTreeSelection *selection,
|
|
ThunarRenamerDialog *renamer_dialog);
|
|
static ThunarFile *
|
|
thunar_renamer_dialog_get_current_directory (ThunarRenamerDialog *renamer_dialog);
|
|
static void
|
|
thunar_renamer_dialog_set_current_directory (ThunarRenamerDialog *renamer_dialog,
|
|
ThunarFile *current_directory);
|
|
static GList *
|
|
thunar_renamer_dialog_get_selected_files (ThunarRenamerDialog *renamer_dialog);
|
|
static gboolean
|
|
thunar_renamer_dialog_get_standalone (ThunarRenamerDialog *renamer_dialog);
|
|
static void
|
|
thunar_renamer_dialog_set_standalone (ThunarRenamerDialog *renamer_dialog,
|
|
gboolean fixed);
|
|
static GtkWidget *
|
|
thunar_renamer_dialog_append_menu_item (ThunarRenamerDialog *renamer_dialog,
|
|
GtkMenuShell *menu,
|
|
ThunarRenamerAction action);
|
|
static gboolean
|
|
thunar_renamer_configure_event (GtkWidget *widget,
|
|
GdkEventConfigure *event);
|
|
static void
|
|
thunar_renamer_save_geometry_timer_destroy (gpointer user_data);
|
|
|
|
|
|
|
|
struct _ThunarRenamerDialogClass
|
|
{
|
|
ThunarAbstractDialogClass __parent__;
|
|
};
|
|
|
|
struct _ThunarRenamerDialog
|
|
{
|
|
ThunarAbstractDialog __parent__;
|
|
|
|
/* need to grab a reference on the icon factory for the standalone version */
|
|
ThunarIconFactory *icon_factory;
|
|
|
|
ThunarRenamerModel *model;
|
|
|
|
ThunarActionManager *action_mgr;
|
|
|
|
GtkWidget *close_button;
|
|
|
|
GtkWidget *tree_view;
|
|
GtkWidget *progress;
|
|
|
|
GtkWidget *mode_combo;
|
|
|
|
GtkTreeViewColumn *name_column;
|
|
|
|
/* the current directory used for the "Add Files" dialog */
|
|
ThunarFile *current_directory;
|
|
|
|
/* #GList of currently selected #ThunarFile<!---->s */
|
|
GList *selected_files;
|
|
|
|
/* whether the dialog should act like a standalone application */
|
|
gboolean standalone;
|
|
|
|
/* TRUE while drop highlighting */
|
|
gboolean drag_highlighted;
|
|
|
|
/* support to remember window geometry */
|
|
guint save_geometry_timer_id;
|
|
};
|
|
|
|
|
|
|
|
/* clang-format off */
|
|
static XfceGtkActionEntry thunar_renamer_action_entries[] =
|
|
{
|
|
{ THUNAR_RENAMER_ACTION_ADD_FILES, "<Actions>/ThunarRenamerDialog/add-files", "", XFCE_GTK_IMAGE_MENU_ITEM, N_ ("_Add Files..."), N_ ("Include additional files in the list of files to be renamed"), "list-add", G_CALLBACK (thunar_renamer_dialog_action_add_files), },
|
|
{ THUNAR_RENAMER_ACTION_REMOVE_FILES, "<Actions>/ThunarRenamerDialog/remove-files", "", XFCE_GTK_IMAGE_MENU_ITEM, NULL, NULL, "list-remove", G_CALLBACK (thunar_renamer_dialog_action_remove_files),},
|
|
{ THUNAR_RENAMER_ACTION_CLEAR, "", "", XFCE_GTK_IMAGE_MENU_ITEM, N_ ("Clear"), N_ ("Clear the file list below"), "edit-clear", G_CALLBACK (thunar_renamer_dialog_action_clear), },
|
|
{ THUNAR_RENAMER_ACTION_ABOUT, "", "", XFCE_GTK_IMAGE_MENU_ITEM, N_ ("_About"), N_ ("Display information about Thunar Bulk Rename"), "help-about", G_CALLBACK (thunar_renamer_dialog_action_about), },
|
|
};
|
|
/* clang-format on */
|
|
|
|
#define get_action_entry(id) xfce_gtk_get_action_entry_by_id (thunar_renamer_action_entries, G_N_ELEMENTS (thunar_renamer_action_entries), id)
|
|
|
|
/* Target types for dropping to the tree view */
|
|
static const GtkTargetEntry drop_targets[] = {
|
|
{ "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_WIDGET, TARGET_GTK_TREE_MODEL_ROW },
|
|
{ "text/uri-list", 0, TARGET_TEXT_URI_LIST }
|
|
};
|
|
|
|
/* Target types for dragging files in the tree view */
|
|
static const GtkTargetEntry drag_targets[] = {
|
|
{ "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_WIDGET, TARGET_GTK_TREE_MODEL_ROW }
|
|
};
|
|
|
|
|
|
|
|
G_DEFINE_TYPE (ThunarRenamerDialog, thunar_renamer_dialog, THUNAR_TYPE_ABSTRACT_DIALOG)
|
|
|
|
|
|
|
|
static void
|
|
thunar_renamer_dialog_class_init (ThunarRenamerDialogClass *klass)
|
|
{
|
|
GtkDialogClass *gtkdialog_class;
|
|
GtkWidgetClass *gtkwidget_class;
|
|
GObjectClass *gobject_class;
|
|
|
|
gobject_class = G_OBJECT_CLASS (klass);
|
|
gobject_class->dispose = thunar_renamer_dialog_dispose;
|
|
gobject_class->finalize = thunar_renamer_dialog_finalize;
|
|
gobject_class->get_property = thunar_renamer_dialog_get_property;
|
|
gobject_class->set_property = thunar_renamer_dialog_set_property;
|
|
|
|
gtkwidget_class = GTK_WIDGET_CLASS (klass);
|
|
gtkwidget_class->realize = thunar_renamer_dialog_realize;
|
|
gtkwidget_class->unrealize = thunar_renamer_dialog_unrealize;
|
|
gtkwidget_class->configure_event = thunar_renamer_configure_event;
|
|
|
|
gtkdialog_class = GTK_DIALOG_CLASS (klass);
|
|
gtkdialog_class->response = thunar_renamer_dialog_response;
|
|
|
|
/**
|
|
* ThunarRenamerDialog:current-directory:
|
|
*
|
|
* The current directory of the #ThunarRenamerDialog,
|
|
* which is the default directory for the "Add Files"
|
|
* dialog. May also be %NULL in which case the default
|
|
* current directory of the file manager process will
|
|
* be used.
|
|
**/
|
|
g_object_class_install_property (gobject_class,
|
|
PROP_CURRENT_DIRECTORY,
|
|
g_param_spec_object ("current-directory",
|
|
"current-directory",
|
|
"current-directory",
|
|
THUNAR_TYPE_FILE,
|
|
EXO_PARAM_READWRITE));
|
|
|
|
/**
|
|
* ThunarRenamerDialog:selected-files:
|
|
*
|
|
* The list of currently selected #ThunarFile<!---->s
|
|
* in this #ThunarRenamerDialog.
|
|
**/
|
|
g_object_class_install_property (gobject_class,
|
|
PROP_SELECTED_FILES,
|
|
g_param_spec_boxed ("selected-files",
|
|
"selected-files",
|
|
"selected-files",
|
|
THUNARX_TYPE_FILE_INFO_LIST,
|
|
EXO_PARAM_READABLE));
|
|
|
|
/**
|
|
* ThunarRenamerDialog:standalone:
|
|
*
|
|
* %TRUE if the #ThunarRenamerDialog should display a
|
|
* toolbar and add several actions which are only
|
|
* useful in a standalone fashion.
|
|
*
|
|
* If the #ThunarRenamerDialog is opened from within
|
|
* Thunar this property will always be %FALSE.
|
|
**/
|
|
g_object_class_install_property (gobject_class,
|
|
PROP_STANDALONE,
|
|
g_param_spec_boolean ("standalone",
|
|
"standalone",
|
|
"standalone",
|
|
FALSE,
|
|
EXO_PARAM_READWRITE));
|
|
}
|
|
|
|
|
|
|
|
static gint
|
|
trd_renamer_compare (gconstpointer a,
|
|
gconstpointer b)
|
|
{
|
|
return g_utf8_collate (thunarx_renamer_get_name (THUNARX_RENAMER (a)),
|
|
thunarx_renamer_get_name (THUNARX_RENAMER (b)));
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
thunar_renamer_dialog_init (ThunarRenamerDialog *renamer_dialog)
|
|
{
|
|
ThunarxProviderFactory *provider_factory;
|
|
ThunarPreferences *preferences;
|
|
GtkTreeViewColumn *column;
|
|
GtkTreeSelection *selection;
|
|
GtkCellRenderer *renderer;
|
|
GtkSizeGroup *size_group;
|
|
const gchar *active_str;
|
|
GHashTable *settings;
|
|
GEnumClass *klass;
|
|
GtkWidget *notebook;
|
|
GtkWidget *toolbar;
|
|
GtkToolItem *seperator;
|
|
GtkWidget *button;
|
|
GtkWidget *mcombo;
|
|
GtkWidget *rcombo;
|
|
GtkWidget *frame;
|
|
GtkWidget *image;
|
|
GtkWidget *label;
|
|
GtkWidget *mbox;
|
|
GtkWidget *rbox;
|
|
GtkWidget *vbox;
|
|
GtkWidget *swin;
|
|
GtkWidget *infobar;
|
|
XfceRc *rc;
|
|
gchar **entries;
|
|
GList *providers;
|
|
GList *renamers = NULL;
|
|
GList *lp;
|
|
gint active = 0;
|
|
gint last_dialog_width;
|
|
gint last_dialog_height;
|
|
gint last_dialog_maximized;
|
|
guint n;
|
|
|
|
/* allocate a new bulk rename model */
|
|
renamer_dialog->model = thunar_renamer_model_new ();
|
|
|
|
xfce_gtk_translate_action_entries (thunar_renamer_action_entries, G_N_ELEMENTS (thunar_renamer_action_entries));
|
|
|
|
/* determine the list of available renamers (using the renamer providers) */
|
|
provider_factory = thunarx_provider_factory_get_default ();
|
|
providers = thunarx_provider_factory_list_providers (provider_factory, THUNARX_TYPE_RENAMER_PROVIDER);
|
|
for (lp = providers; lp != NULL; lp = lp->next)
|
|
{
|
|
/* determine the renamers for this provider */
|
|
renamers = g_list_concat (renamers, thunarx_renamer_provider_get_renamers (lp->data));
|
|
|
|
/* release this provider */
|
|
g_object_unref (G_OBJECT (lp->data));
|
|
}
|
|
g_object_unref (G_OBJECT (provider_factory));
|
|
g_list_free (providers);
|
|
|
|
/* initialize the dialog */
|
|
preferences = thunar_preferences_get ();
|
|
g_object_get (G_OBJECT (preferences),
|
|
"last-renamer-dialog-width", &last_dialog_width,
|
|
"last-renamer-dialog-height", &last_dialog_height,
|
|
"last-renamer-dialog-maximized", &last_dialog_maximized,
|
|
NULL);
|
|
g_object_unref (preferences);
|
|
gtk_window_set_default_size (GTK_WINDOW (renamer_dialog), last_dialog_width, last_dialog_height);
|
|
|
|
/* restore the maxized state of the dialog */
|
|
if (G_UNLIKELY (last_dialog_maximized))
|
|
gtk_window_maximize (GTK_WINDOW (renamer_dialog));
|
|
|
|
gtk_window_set_title (GTK_WINDOW (renamer_dialog), _("Rename Multiple Files"));
|
|
|
|
/* add the "Close" button */
|
|
renamer_dialog->close_button = gtk_dialog_add_button (GTK_DIALOG (renamer_dialog), _("_Close"), GTK_RESPONSE_DELETE_EVENT);
|
|
gtk_widget_set_tooltip_text (renamer_dialog->close_button, _("Close this window, cancel further changes."));
|
|
|
|
/* add the "Rename" button */
|
|
button = gtk_dialog_add_button (GTK_DIALOG (renamer_dialog), _("_Rename"), GTK_RESPONSE_APPLY);
|
|
g_object_bind_property (G_OBJECT (renamer_dialog->model), "can-rename",
|
|
G_OBJECT (button), "sensitive",
|
|
G_BINDING_SYNC_CREATE);
|
|
gtk_dialog_set_default_response (GTK_DIALOG (renamer_dialog), GTK_RESPONSE_APPLY);
|
|
gtk_widget_set_tooltip_text (button, _("Rename items listed above, keep this window open."));
|
|
|
|
/* setup the action manager support for this dialog */
|
|
renamer_dialog->action_mgr = g_object_new (THUNAR_TYPE_ACTION_MANAGER, "widget", GTK_WIDGET (renamer_dialog), NULL);
|
|
g_object_bind_property (G_OBJECT (renamer_dialog), "selected-files", G_OBJECT (renamer_dialog->action_mgr), "selected-files", G_BINDING_SYNC_CREATE);
|
|
g_object_bind_property (G_OBJECT (renamer_dialog), "current-directory", G_OBJECT (renamer_dialog->action_mgr), "current-directory", G_BINDING_SYNC_CREATE);
|
|
|
|
/* add the toolbar to the dialog */
|
|
toolbar = gtk_toolbar_new ();
|
|
xfce_gtk_tool_button_new_from_action_entry (get_action_entry (THUNAR_RENAMER_ACTION_ADD_FILES), G_OBJECT (renamer_dialog), GTK_TOOLBAR (toolbar));
|
|
xfce_gtk_tool_button_new_from_action_entry (get_action_entry (THUNAR_RENAMER_ACTION_REMOVE_FILES), G_OBJECT (renamer_dialog), GTK_TOOLBAR (toolbar));
|
|
seperator = gtk_separator_tool_item_new ();
|
|
gtk_container_add (GTK_CONTAINER (toolbar), GTK_WIDGET (seperator));
|
|
gtk_widget_show (GTK_WIDGET (seperator));
|
|
xfce_gtk_tool_button_new_from_action_entry (get_action_entry (THUNAR_RENAMER_ACTION_CLEAR), G_OBJECT (renamer_dialog), GTK_TOOLBAR (toolbar));
|
|
seperator = gtk_separator_tool_item_new ();
|
|
gtk_container_add (GTK_CONTAINER (toolbar), GTK_WIDGET (seperator));
|
|
gtk_widget_show (GTK_WIDGET (seperator));
|
|
xfce_gtk_tool_button_new_from_action_entry (get_action_entry (THUNAR_RENAMER_ACTION_ABOUT), G_OBJECT (renamer_dialog), GTK_TOOLBAR (toolbar));
|
|
g_object_bind_property (G_OBJECT (renamer_dialog), "standalone", G_OBJECT (toolbar), "visible", G_BINDING_SYNC_CREATE);
|
|
gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (renamer_dialog))), toolbar, FALSE, FALSE, 0);
|
|
gtk_widget_show_all (GTK_WIDGET (toolbar));
|
|
|
|
/* create the main vbox */
|
|
mbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
|
|
gtk_container_set_border_width (GTK_CONTAINER (mbox), 6);
|
|
gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (renamer_dialog))), mbox, TRUE, TRUE, 0);
|
|
gtk_widget_show (mbox);
|
|
|
|
/* create the scrolled window for the tree view */
|
|
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_ETCHED_IN);
|
|
gtk_box_pack_start (GTK_BOX (mbox), swin, TRUE, TRUE, 0);
|
|
gtk_widget_show (swin);
|
|
|
|
/* create the tree view */
|
|
renamer_dialog->tree_view = gtk_tree_view_new_with_model (GTK_TREE_MODEL (renamer_dialog->model));
|
|
gtk_tree_view_set_search_column (GTK_TREE_VIEW (renamer_dialog->tree_view), THUNAR_RENAMER_MODEL_COLUMN_OLDNAME);
|
|
gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW (renamer_dialog->tree_view), TRUE);
|
|
gtk_tree_view_set_enable_search (GTK_TREE_VIEW (renamer_dialog->tree_view), TRUE);
|
|
gtk_tree_view_set_rubber_banding (GTK_TREE_VIEW (renamer_dialog->tree_view), TRUE);
|
|
g_signal_connect (G_OBJECT (renamer_dialog->tree_view), "button-press-event", G_CALLBACK (thunar_renamer_dialog_button_press_event), renamer_dialog);
|
|
g_signal_connect (G_OBJECT (renamer_dialog->tree_view), "popup-menu", G_CALLBACK (thunar_renamer_dialog_popup_menu), renamer_dialog);
|
|
g_signal_connect (G_OBJECT (renamer_dialog->tree_view), "row-activated", G_CALLBACK (thunar_renamer_dialog_row_activated), renamer_dialog);
|
|
gtk_container_add (GTK_CONTAINER (swin), renamer_dialog->tree_view);
|
|
gtk_widget_show (renamer_dialog->tree_view);
|
|
|
|
/* create the tree view column for the old file name */
|
|
renamer_dialog->name_column = column = gtk_tree_view_column_new ();
|
|
gtk_tree_view_column_set_spacing (column, 2);
|
|
gtk_tree_view_column_set_min_width (column, 100);
|
|
gtk_tree_view_column_set_title (column, _("Name"));
|
|
gtk_tree_view_column_set_resizable (column, TRUE);
|
|
gtk_tree_view_column_set_fixed_width (column, 250);
|
|
gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
|
|
gtk_tree_view_column_set_clickable (column, TRUE);
|
|
g_signal_connect (G_OBJECT (column), "clicked", G_CALLBACK (thunar_renamer_dialog_name_column_clicked), renamer_dialog);
|
|
renderer = g_object_new (THUNAR_TYPE_ICON_RENDERER, "size", 16, NULL);
|
|
gtk_tree_view_column_pack_start (column, renderer, FALSE);
|
|
gtk_tree_view_column_set_attributes (column, renderer, "file", THUNAR_RENAMER_MODEL_COLUMN_FILE, NULL);
|
|
renderer = g_object_new (GTK_TYPE_CELL_RENDERER_TEXT, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
|
|
gtk_tree_view_column_pack_start (column, renderer, TRUE);
|
|
gtk_tree_view_column_set_attributes (column, renderer, "text", THUNAR_RENAMER_MODEL_COLUMN_OLDNAME, NULL);
|
|
gtk_tree_view_append_column (GTK_TREE_VIEW (renamer_dialog->tree_view), column);
|
|
|
|
/* create the tree view column for the new file name */
|
|
column = gtk_tree_view_column_new ();
|
|
gtk_tree_view_column_set_min_width (column, 75);
|
|
gtk_tree_view_column_set_resizable (column, TRUE);
|
|
gtk_tree_view_column_set_title (column, _("New Name"));
|
|
gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
|
|
renderer = g_object_new (GTK_TYPE_CELL_RENDERER_TEXT,
|
|
"ellipsize", PANGO_ELLIPSIZE_END,
|
|
"foreground", "Red",
|
|
NULL);
|
|
gtk_tree_view_column_pack_start (column, renderer, TRUE);
|
|
gtk_tree_view_column_set_attributes (column, renderer,
|
|
"foreground-set", THUNAR_RENAMER_MODEL_COLUMN_CONFLICT,
|
|
"text", THUNAR_RENAMER_MODEL_COLUMN_NEWNAME,
|
|
"weight", THUNAR_RENAMER_MODEL_COLUMN_CONFLICT_WEIGHT,
|
|
NULL);
|
|
gtk_tree_view_append_column (GTK_TREE_VIEW (renamer_dialog->tree_view), column);
|
|
|
|
/* create the vbox for the renamer parameters */
|
|
vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
|
|
gtk_box_pack_start (GTK_BOX (mbox), vbox, FALSE, FALSE, 0);
|
|
gtk_widget_show (vbox);
|
|
|
|
/* create the hbox for the renamer selection */
|
|
rbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 3);
|
|
gtk_box_pack_start (GTK_BOX (vbox), rbox, FALSE, FALSE, 0);
|
|
gtk_widget_show (rbox);
|
|
|
|
/* create the rename progress bar */
|
|
renamer_dialog->progress = thunar_renamer_progress_new ();
|
|
gtk_box_pack_start (GTK_BOX (vbox), renamer_dialog->progress, FALSE, FALSE, 0);
|
|
g_object_bind_property (G_OBJECT (renamer_dialog->progress), "visible", G_OBJECT (rbox), "visible", G_BINDING_INVERT_BOOLEAN | G_BINDING_SYNC_CREATE);
|
|
g_object_bind_property (G_OBJECT (renamer_dialog->progress), "visible", G_OBJECT (swin), "sensitive", G_BINDING_INVERT_BOOLEAN | G_BINDING_SYNC_CREATE);
|
|
g_object_bind_property (G_OBJECT (renamer_dialog->progress), "visible", G_OBJECT (toolbar), "sensitive", G_BINDING_INVERT_BOOLEAN | G_BINDING_SYNC_CREATE);
|
|
g_object_bind_property (G_OBJECT (renamer_dialog->progress), "visible", G_OBJECT (renamer_dialog->model), "frozen", G_BINDING_SYNC_CREATE);
|
|
|
|
/* synchronize the height of the progress bar and the rbox with the combos */
|
|
size_group = gtk_size_group_new (GTK_SIZE_GROUP_VERTICAL);
|
|
gtk_size_group_add_widget (size_group, renamer_dialog->progress);
|
|
gtk_size_group_add_widget (size_group, rbox);
|
|
g_object_unref (G_OBJECT (size_group));
|
|
|
|
/* create the frame for the renamer widgets */
|
|
frame = g_object_new (GTK_TYPE_FRAME, "shadow-type", GTK_SHADOW_ETCHED_IN, NULL);
|
|
g_object_bind_property (G_OBJECT (renamer_dialog->progress), "visible", G_OBJECT (frame), "sensitive", G_BINDING_INVERT_BOOLEAN | G_BINDING_SYNC_CREATE);
|
|
gtk_box_pack_start (GTK_BOX (vbox), frame, TRUE, TRUE, 0);
|
|
gtk_widget_show (frame);
|
|
|
|
renamer_dialog->mode_combo = NULL;
|
|
|
|
/* check if we have any renamers */
|
|
if (G_LIKELY (renamers != NULL))
|
|
{
|
|
/* sort the renamers by name */
|
|
renamers = g_list_sort (renamers, trd_renamer_compare);
|
|
|
|
/* open the config file, xfce_rc_config_open() should never return NULL */
|
|
rc = xfce_rc_config_open (XFCE_RESOURCE_CONFIG, "Thunar/renamerrc", TRUE);
|
|
xfce_rc_set_group (rc, "Configuration");
|
|
|
|
/* create the renamer combo box for the renamer selection */
|
|
rcombo = gtk_combo_box_text_new ();
|
|
for (lp = renamers; lp != NULL; lp = lp->next)
|
|
gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (rcombo), thunarx_renamer_get_name (lp->data));
|
|
gtk_box_pack_start (GTK_BOX (rbox), rcombo, FALSE, FALSE, 0);
|
|
gtk_widget_show (rcombo);
|
|
|
|
/* add a "Help" button */
|
|
button = gtk_button_new ();
|
|
gtk_widget_set_tooltip_text (button, _("Click here to view the documentation for the selected rename operation."));
|
|
g_signal_connect_swapped (G_OBJECT (button), "clicked", G_CALLBACK (thunar_renamer_dialog_help), renamer_dialog);
|
|
gtk_box_pack_start (GTK_BOX (rbox), button, FALSE, FALSE, 0);
|
|
gtk_widget_show (button);
|
|
|
|
image = gtk_image_new_from_icon_name ("help-browser", GTK_ICON_SIZE_BUTTON);
|
|
gtk_container_add (GTK_CONTAINER (button), image);
|
|
gtk_widget_show (image);
|
|
|
|
/* create the name/extension/both combo box */
|
|
mcombo = gtk_combo_box_text_new ();
|
|
klass = g_type_class_ref (THUNAR_TYPE_RENAMER_MODE);
|
|
active_str = xfce_rc_read_entry_untranslated (rc, "LastActiveMode", "");
|
|
for (active = 0, n = 0; n < klass->n_values; ++n)
|
|
{
|
|
if (g_strcmp0 (active_str, klass->values[n].value_name) == 0)
|
|
active = n;
|
|
gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (mcombo), _(klass->values[n].value_nick));
|
|
}
|
|
g_object_bind_property (G_OBJECT (renamer_dialog->model), "mode", G_OBJECT (mcombo), "active", G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
|
|
gtk_box_pack_end (GTK_BOX (rbox), mcombo, FALSE, FALSE, 0);
|
|
gtk_combo_box_set_active (GTK_COMBO_BOX (mcombo), active);
|
|
g_type_class_unref (klass);
|
|
gtk_widget_show (mcombo);
|
|
renamer_dialog->mode_combo = mcombo;
|
|
|
|
/* allocate the notebook with the renamers */
|
|
notebook = gtk_notebook_new ();
|
|
gtk_notebook_set_show_tabs (GTK_NOTEBOOK (notebook), FALSE);
|
|
gtk_notebook_set_show_border (GTK_NOTEBOOK (notebook), FALSE);
|
|
g_signal_connect (G_OBJECT (notebook), "notify::page", G_CALLBACK (thunar_renamer_dialog_notify_page), renamer_dialog);
|
|
gtk_container_add (GTK_CONTAINER (frame), notebook);
|
|
gtk_widget_show (notebook);
|
|
|
|
/* determine the last active renamer (as string) */
|
|
active_str = xfce_rc_read_entry_untranslated (rc, "LastActiveRenamer", "");
|
|
|
|
/* add the renamers to the notebook */
|
|
for (active = 0, lp = renamers; lp != NULL; lp = lp->next)
|
|
{
|
|
/* append the renamer to the notebook... */
|
|
gtk_notebook_append_page (GTK_NOTEBOOK (notebook), lp->data, NULL);
|
|
|
|
/* check if this page should be active by default */
|
|
if (g_strcmp0 (G_OBJECT_TYPE_NAME (lp->data), active_str) == 0)
|
|
active = g_list_position (renamers, lp);
|
|
|
|
/* try to load the settings for the renamer */
|
|
entries = xfce_rc_get_entries (rc, G_OBJECT_TYPE_NAME (lp->data));
|
|
if (G_LIKELY (entries != NULL))
|
|
{
|
|
/* add the settings to a hash table */
|
|
xfce_rc_set_group (rc, G_OBJECT_TYPE_NAME (lp->data));
|
|
settings = g_hash_table_new (g_str_hash, g_str_equal);
|
|
for (n = 0; entries[n] != NULL; ++n)
|
|
g_hash_table_insert (settings, entries[n], (gchar *) xfce_rc_read_entry_untranslated (rc, entries[n], NULL));
|
|
|
|
/* let the renamer load the settings */
|
|
thunarx_renamer_load (THUNARX_RENAMER (lp->data), settings);
|
|
|
|
/* cleanup */
|
|
g_hash_table_destroy (settings);
|
|
g_strfreev (entries);
|
|
}
|
|
|
|
/* ...and make sure its visible */
|
|
gtk_widget_show (lp->data);
|
|
}
|
|
|
|
/* default to the first renamer */
|
|
gtk_combo_box_set_active (GTK_COMBO_BOX (rcombo), active);
|
|
|
|
/* connect the combo box to the notebook */
|
|
g_object_bind_property (G_OBJECT (rcombo), "active", G_OBJECT (notebook), "page", G_BINDING_SYNC_CREATE);
|
|
|
|
/* close the config handle */
|
|
xfce_rc_close (rc);
|
|
|
|
/* setup drop support for the tree view */
|
|
gtk_drag_dest_set (renamer_dialog->tree_view, 0, drop_targets, G_N_ELEMENTS (drop_targets), GDK_ACTION_COPY | GDK_ACTION_MOVE);
|
|
gtk_drag_source_set (renamer_dialog->tree_view, GDK_BUTTON1_MASK, drag_targets, G_N_ELEMENTS (drag_targets), GDK_ACTION_MOVE);
|
|
g_signal_connect (G_OBJECT (renamer_dialog->tree_view), "drag-data-received", G_CALLBACK (thunar_renamer_dialog_drag_data_received), renamer_dialog);
|
|
g_signal_connect (G_OBJECT (renamer_dialog->tree_view), "drag-leave", G_CALLBACK (thunar_renamer_dialog_drag_leave), renamer_dialog);
|
|
g_signal_connect (G_OBJECT (renamer_dialog->tree_view), "drag-motion", G_CALLBACK (thunar_renamer_dialog_drag_motion), renamer_dialog);
|
|
g_signal_connect (G_OBJECT (renamer_dialog->tree_view), "drag-drop", G_CALLBACK (thunar_renamer_dialog_drag_drop), renamer_dialog);
|
|
|
|
/* setup the tree selection */
|
|
selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (renamer_dialog->tree_view));
|
|
gtk_tree_selection_set_mode (selection, GTK_SELECTION_MULTIPLE);
|
|
g_signal_connect (G_OBJECT (selection), "changed", G_CALLBACK (thunar_renamer_dialog_selection_changed), renamer_dialog);
|
|
thunar_renamer_dialog_selection_changed (selection, renamer_dialog);
|
|
}
|
|
else
|
|
{
|
|
/* make the other widgets insensitive */
|
|
gtk_widget_set_sensitive (toolbar, FALSE);
|
|
gtk_widget_set_sensitive (swin, FALSE);
|
|
|
|
/* display an error to the user */
|
|
infobar = gtk_info_bar_new ();
|
|
gtk_info_bar_set_message_type (GTK_INFO_BAR (infobar), GTK_MESSAGE_ERROR);
|
|
gtk_container_add (GTK_CONTAINER (frame), infobar);
|
|
gtk_widget_show (infobar);
|
|
|
|
image = gtk_image_new_from_icon_name ("dialog-error", GTK_ICON_SIZE_DIALOG);
|
|
gtk_container_add (GTK_CONTAINER (gtk_info_bar_get_content_area (GTK_INFO_BAR (infobar))), image);
|
|
gtk_widget_show (image);
|
|
|
|
/* TRANSLATORS: You can test this string by temporarily removing thunar-sbr.* from $libdir/thunarx-2/,
|
|
* and opening the multi rename dialog by selecting multiple files and pressing F2.
|
|
*/
|
|
label = gtk_label_new (_("No renamer modules were found on your system. Please check your\n"
|
|
"installation or contact your system administrator. If you install Thunar\n"
|
|
"from source, be sure to enable the \"Simple Builtin Renamers\" plugin."));
|
|
gtk_label_set_xalign (GTK_LABEL (label), 0.0f);
|
|
gtk_label_set_selectable (GTK_LABEL (label), TRUE);
|
|
gtk_container_add (GTK_CONTAINER (gtk_info_bar_get_content_area (GTK_INFO_BAR (infobar))), label);
|
|
gtk_widget_show (label);
|
|
}
|
|
|
|
/* release the renamers list */
|
|
g_list_free_full (renamers, g_object_unref);
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
thunar_renamer_dialog_dispose (GObject *object)
|
|
{
|
|
ThunarRenamerDialog *renamer_dialog = THUNAR_RENAMER_DIALOG (object);
|
|
|
|
/* destroy the save geometry timer source */
|
|
if (G_UNLIKELY (renamer_dialog->save_geometry_timer_id != 0))
|
|
g_source_remove (renamer_dialog->save_geometry_timer_id);
|
|
|
|
/* reset the "current-directory" property */
|
|
thunar_renamer_dialog_set_current_directory (renamer_dialog, NULL);
|
|
|
|
(*G_OBJECT_CLASS (thunar_renamer_dialog_parent_class)->dispose) (object);
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
thunar_renamer_dialog_finalize (GObject *object)
|
|
{
|
|
ThunarRenamerDialog *renamer_dialog = THUNAR_RENAMER_DIALOG (object);
|
|
|
|
/* release the action manager support */
|
|
g_object_unref (G_OBJECT (renamer_dialog->action_mgr));
|
|
|
|
/* release our bulk rename model */
|
|
g_object_unref (G_OBJECT (renamer_dialog->model));
|
|
|
|
/* release the list of selected files */
|
|
thunarx_file_info_list_free (renamer_dialog->selected_files);
|
|
|
|
(*G_OBJECT_CLASS (thunar_renamer_dialog_parent_class)->finalize) (object);
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
thunar_renamer_dialog_get_property (GObject *object,
|
|
guint prop_id,
|
|
GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
ThunarRenamerDialog *renamer_dialog = THUNAR_RENAMER_DIALOG (object);
|
|
|
|
switch (prop_id)
|
|
{
|
|
case PROP_CURRENT_DIRECTORY:
|
|
g_value_set_object (value, thunar_renamer_dialog_get_current_directory (renamer_dialog));
|
|
break;
|
|
|
|
case PROP_SELECTED_FILES:
|
|
g_value_set_boxed (value, thunar_renamer_dialog_get_selected_files (renamer_dialog));
|
|
break;
|
|
|
|
case PROP_STANDALONE:
|
|
g_value_set_boolean (value, thunar_renamer_dialog_get_standalone (renamer_dialog));
|
|
break;
|
|
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
thunar_renamer_dialog_set_property (GObject *object,
|
|
guint prop_id,
|
|
const GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
ThunarRenamerDialog *renamer_dialog = THUNAR_RENAMER_DIALOG (object);
|
|
|
|
switch (prop_id)
|
|
{
|
|
case PROP_CURRENT_DIRECTORY:
|
|
thunar_renamer_dialog_set_current_directory (renamer_dialog, g_value_get_object (value));
|
|
break;
|
|
|
|
case PROP_STANDALONE:
|
|
thunar_renamer_dialog_set_standalone (renamer_dialog, g_value_get_boolean (value));
|
|
break;
|
|
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
thunar_renamer_dialog_realize (GtkWidget *widget)
|
|
{
|
|
ThunarRenamerDialog *renamer_dialog = THUNAR_RENAMER_DIALOG (widget);
|
|
GtkIconTheme *icon_theme;
|
|
|
|
/* realize the widget */
|
|
(*GTK_WIDGET_CLASS (thunar_renamer_dialog_parent_class)->realize) (widget);
|
|
|
|
/* grab a reference on the icon factory for the screen */
|
|
icon_theme = gtk_icon_theme_get_for_screen (gtk_widget_get_screen (widget));
|
|
renamer_dialog->icon_factory = thunar_icon_factory_get_for_icon_theme (icon_theme);
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
thunar_renamer_dialog_unrealize (GtkWidget *widget)
|
|
{
|
|
ThunarRenamerDialog *renamer_dialog = THUNAR_RENAMER_DIALOG (widget);
|
|
|
|
/* release the reference on the icon factory */
|
|
g_object_unref (G_OBJECT (renamer_dialog->icon_factory));
|
|
renamer_dialog->icon_factory = NULL;
|
|
|
|
/* unrealize the dialog */
|
|
(*GTK_WIDGET_CLASS (thunar_renamer_dialog_parent_class)->unrealize) (widget);
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
thunar_renamer_dialog_response (GtkDialog *dialog,
|
|
gint response)
|
|
{
|
|
ThunarRenamerDialog *renamer_dialog = THUNAR_RENAMER_DIALOG (dialog);
|
|
GtkTreeIter iter;
|
|
ThunarFile *file;
|
|
GList *pair_list = NULL;
|
|
gchar *name;
|
|
|
|
/* grab an additional reference on the dialog */
|
|
g_object_ref (G_OBJECT (dialog));
|
|
|
|
/* check if we should rename */
|
|
if (G_LIKELY (response == GTK_RESPONSE_APPLY))
|
|
{
|
|
/* save the current settings */
|
|
thunar_renamer_dialog_save (renamer_dialog);
|
|
|
|
/* display the rename progress bar */
|
|
gtk_widget_show (renamer_dialog->progress);
|
|
|
|
/* determine the iterator for the first item */
|
|
if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (renamer_dialog->model), &iter))
|
|
{
|
|
/* determine the tree row references for the rows that should be renamed */
|
|
do
|
|
{
|
|
/* determine the file and newname for the row */
|
|
gtk_tree_model_get (GTK_TREE_MODEL (renamer_dialog->model), &iter,
|
|
THUNAR_RENAMER_MODEL_COLUMN_NEWNAME, &name,
|
|
THUNAR_RENAMER_MODEL_COLUMN_FILE, &file,
|
|
-1);
|
|
|
|
/* check if this row should be renamed */
|
|
if (G_LIKELY (name != NULL && *name != '\0'))
|
|
pair_list = g_list_prepend (pair_list, thunar_renamer_pair_new (file, name));
|
|
|
|
/* cleanup */
|
|
g_object_unref (file);
|
|
g_free (name);
|
|
}
|
|
while (gtk_tree_model_iter_next (GTK_TREE_MODEL (renamer_dialog->model), &iter));
|
|
}
|
|
|
|
/* perform the rename (returns when done) */
|
|
thunar_renamer_progress_run (THUNAR_RENAMER_PROGRESS (renamer_dialog->progress), pair_list);
|
|
|
|
/* hide the rename progress bar (unless the dialog has been destroyed) */
|
|
if (gtk_widget_get_realized (GTK_WIDGET (renamer_dialog)))
|
|
gtk_widget_hide (renamer_dialog->progress);
|
|
|
|
/* release the pairs */
|
|
thunar_renamer_pair_list_free (pair_list);
|
|
}
|
|
else if (thunar_renamer_progress_running (THUNAR_RENAMER_PROGRESS (renamer_dialog->progress)))
|
|
{
|
|
/* cancel the rename operation */
|
|
thunar_renamer_progress_cancel (THUNAR_RENAMER_PROGRESS (renamer_dialog->progress));
|
|
}
|
|
else
|
|
{
|
|
/* hide and destroy the dialog window */
|
|
gtk_widget_hide (GTK_WIDGET (dialog));
|
|
gtk_widget_destroy (GTK_WIDGET (dialog));
|
|
}
|
|
|
|
/* release the additional reference on the dialog */
|
|
g_object_unref (G_OBJECT (dialog));
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* thunar_renamer_dialog_append_menu_item:
|
|
* @renamer_dialog : Instance of a #ThunarRenamerDialog
|
|
* @menu : #GtkMenuShell to which the item should be added
|
|
* @action : #ThunarRenamerAction to select which item should be added
|
|
*
|
|
* Adds the selected, widget specific #GtkMenuItem to the passed #GtkMenuShell
|
|
*
|
|
* Return value: (transfer none): The added #GtkMenuItem
|
|
**/
|
|
static GtkWidget *
|
|
thunar_renamer_dialog_append_menu_item (ThunarRenamerDialog *renamer_dialog,
|
|
GtkMenuShell *menu,
|
|
ThunarRenamerAction action)
|
|
{
|
|
gchar *label_text;
|
|
gchar *tooltip_text;
|
|
const XfceGtkActionEntry *action_entry = get_action_entry (action);
|
|
GtkWidget *item;
|
|
|
|
_thunar_return_val_if_fail (THUNAR_IS_RENAMER_DIALOG (renamer_dialog), NULL);
|
|
_thunar_return_val_if_fail (action_entry != NULL, NULL);
|
|
|
|
switch (action)
|
|
{
|
|
case THUNAR_RENAMER_ACTION_REMOVE_FILES:
|
|
label_text = ngettext ("Remove File", "Remove Files", g_list_length (renamer_dialog->selected_files));
|
|
tooltip_text = ngettext ("Remove the selected file from the list of files to be renamed",
|
|
"Remove the selected files from the list of files to be renamed", g_list_length (renamer_dialog->selected_files));
|
|
item = xfce_gtk_image_menu_item_new_from_icon_name (label_text, tooltip_text, action_entry->accel_path, action_entry->callback,
|
|
G_OBJECT (renamer_dialog), action_entry->menu_item_icon_name, GTK_MENU_SHELL (menu));
|
|
gtk_widget_set_sensitive (item, renamer_dialog->selected_files != NULL);
|
|
return item;
|
|
default:
|
|
return xfce_gtk_menu_item_new_from_action_entry (action_entry, G_OBJECT (renamer_dialog), menu);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
thunar_renamer_dialog_context_menu (ThunarRenamerDialog *renamer_dialog)
|
|
{
|
|
ThunarxRenamer *renamer;
|
|
ThunarMenu *menu;
|
|
GList *items = NULL;
|
|
GList *lp = NULL;
|
|
|
|
_thunar_return_if_fail (THUNAR_IS_RENAMER_DIALOG (renamer_dialog));
|
|
|
|
/* grab an additional reference on the dialog */
|
|
g_object_ref (G_OBJECT (renamer_dialog));
|
|
|
|
menu = g_object_new (THUNAR_TYPE_MENU, "menu-type", THUNAR_MENU_TYPE_CONTEXT_RENAMER,
|
|
"action_mgr", renamer_dialog->action_mgr,
|
|
"tab-support-disabled", TRUE,
|
|
"change_directory-support-disabled", TRUE, NULL);
|
|
thunar_menu_add_sections (menu, THUNAR_MENU_SECTION_OPEN | THUNAR_MENU_SECTION_SENDTO);
|
|
|
|
/* determine the active renamer */
|
|
renamer = thunar_renamer_model_get_renamer (renamer_dialog->model);
|
|
if (G_LIKELY (renamer != NULL))
|
|
{
|
|
/* determine the menu items provided by the active renamer */
|
|
items = thunarx_renamer_get_menu_items (renamer, GTK_WINDOW (renamer_dialog), renamer_dialog->selected_files);
|
|
if (items != NULL)
|
|
{
|
|
for (lp = items; lp != NULL; lp = lp->next)
|
|
thunar_gtk_menu_thunarx_menu_item_new (lp->data, GTK_MENU_SHELL (menu));
|
|
g_list_free (items);
|
|
xfce_gtk_menu_append_separator (GTK_MENU_SHELL (menu));
|
|
}
|
|
}
|
|
thunar_renamer_dialog_append_menu_item (renamer_dialog, GTK_MENU_SHELL (menu), THUNAR_RENAMER_ACTION_ADD_FILES);
|
|
thunar_renamer_dialog_append_menu_item (renamer_dialog, GTK_MENU_SHELL (menu), THUNAR_RENAMER_ACTION_REMOVE_FILES);
|
|
xfce_gtk_menu_append_separator (GTK_MENU_SHELL (menu));
|
|
thunar_menu_add_sections (menu, THUNAR_MENU_SECTION_PROPERTIES);
|
|
thunar_gtk_menu_hide_accel_labels (GTK_MENU (menu));
|
|
gtk_widget_show_all (GTK_WIDGET (menu));
|
|
|
|
thunar_gtk_menu_run (GTK_MENU (menu));
|
|
|
|
/* release the additional reference on the dialog */
|
|
g_object_unref (G_OBJECT (renamer_dialog));
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
thunar_renamer_dialog_help (ThunarRenamerDialog *renamer_dialog)
|
|
{
|
|
ThunarxRenamer *renamer;
|
|
const gchar *help_url = NULL;
|
|
GError *error = NULL;
|
|
gboolean uri_launched = FALSE;
|
|
|
|
|
|
_thunar_return_if_fail (THUNAR_IS_RENAMER_DIALOG (renamer_dialog));
|
|
|
|
/* check if the active renamer provides a help URL */
|
|
renamer = thunar_renamer_model_get_renamer (renamer_dialog->model);
|
|
if (G_LIKELY (renamer != NULL))
|
|
help_url = thunarx_renamer_get_help_url (renamer);
|
|
|
|
/* check if we have a specific URL */
|
|
if (G_LIKELY (help_url == NULL))
|
|
{
|
|
/* open the general documentation if no specific URL */
|
|
xfce_dialog_show_help (GTK_WINDOW (renamer_dialog), "thunar",
|
|
"bulk-renamer/start", NULL);
|
|
}
|
|
else
|
|
{
|
|
/* try to launch the specific URL */
|
|
uri_launched = gtk_show_uri_on_window (GTK_WINDOW (renamer_dialog),
|
|
help_url,
|
|
gtk_get_current_event_time (),
|
|
&error);
|
|
|
|
if (!uri_launched)
|
|
{
|
|
/* tell the user that we failed */
|
|
thunar_dialogs_show_error (GTK_WIDGET (renamer_dialog), error, _("Failed to open the documentation browser"));
|
|
|
|
/* release the error */
|
|
g_error_free (error);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
trd_save_foreach (gpointer key,
|
|
gpointer value,
|
|
gpointer user_data)
|
|
{
|
|
xfce_rc_write_entry (user_data, key, value);
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
thunar_renamer_dialog_save (ThunarRenamerDialog *renamer_dialog)
|
|
{
|
|
ThunarxRenamer *renamer;
|
|
GHashTable *settings;
|
|
GEnumClass *klass;
|
|
GEnumValue *value;
|
|
XfceRc *rc;
|
|
|
|
_thunar_return_if_fail (THUNAR_IS_RENAMER_DIALOG (renamer_dialog));
|
|
|
|
/* try to open the config handle for writing */
|
|
rc = xfce_rc_config_open (XFCE_RESOURCE_CONFIG, "Thunar/renamerrc", FALSE);
|
|
if (G_UNLIKELY (rc == NULL))
|
|
return;
|
|
|
|
/* determine the currently active renamer */
|
|
renamer = thunar_renamer_model_get_renamer (renamer_dialog->model);
|
|
if (G_LIKELY (renamer != NULL))
|
|
{
|
|
/* save general settings first */
|
|
xfce_rc_set_group (rc, "Configuration");
|
|
|
|
/* remember current mode as last active */
|
|
klass = g_type_class_ref (THUNAR_TYPE_RENAMER_MODE);
|
|
value = g_enum_get_value (klass, thunar_renamer_model_get_mode (renamer_dialog->model));
|
|
if (G_LIKELY (value != NULL))
|
|
xfce_rc_write_entry (rc, "LastActiveMode", value->value_name);
|
|
g_type_class_unref (klass);
|
|
|
|
/* remember renamer as last active */
|
|
xfce_rc_write_entry (rc, "LastActiveRenamer", G_OBJECT_TYPE_NAME (renamer));
|
|
|
|
/* save the renamer's settings */
|
|
settings = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
|
|
xfce_rc_delete_group (rc, G_OBJECT_TYPE_NAME (renamer), TRUE);
|
|
xfce_rc_set_group (rc, G_OBJECT_TYPE_NAME (renamer));
|
|
thunarx_renamer_save (renamer, settings);
|
|
g_hash_table_foreach (settings, trd_save_foreach, rc);
|
|
g_hash_table_destroy (settings);
|
|
}
|
|
|
|
/* close the config handle */
|
|
xfce_rc_close (rc);
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
|
trd_audio_filter_func (const GtkFileFilterInfo *info,
|
|
gpointer data)
|
|
{
|
|
/* check if the MIME type is an audio MIME type */
|
|
return (strncmp (info->mime_type, "audio/", 6) == 0);
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
|
trd_video_filter_func (const GtkFileFilterInfo *info,
|
|
gpointer data)
|
|
{
|
|
/* check if the MIME type is a video MIME type */
|
|
return (strncmp (info->mime_type, "video/", 6) == 0);
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
|
thunar_renamer_dialog_action_add_files (ThunarRenamerDialog *renamer_dialog)
|
|
{
|
|
GtkFileFilter *filter;
|
|
ThunarFile *file;
|
|
GtkWidget *chooser;
|
|
GSList *uris;
|
|
GSList *lp;
|
|
gchar *uri;
|
|
|
|
_thunar_return_val_if_fail (THUNAR_IS_RENAMER_DIALOG (renamer_dialog), FALSE);
|
|
|
|
/* allocate the file chooser */
|
|
chooser = gtk_file_chooser_dialog_new (_("Select files to rename"),
|
|
GTK_WINDOW (renamer_dialog),
|
|
GTK_FILE_CHOOSER_ACTION_OPEN,
|
|
_("_Cancel"), GTK_RESPONSE_CANCEL,
|
|
_("_Open"), GTK_RESPONSE_ACCEPT,
|
|
NULL);
|
|
gtk_file_chooser_set_local_only (GTK_FILE_CHOOSER (chooser), TRUE);
|
|
gtk_file_chooser_set_select_multiple (GTK_FILE_CHOOSER (chooser), TRUE);
|
|
|
|
/* add file chooser filers */
|
|
filter = gtk_file_filter_new ();
|
|
gtk_file_filter_set_name (filter, _("All Files"));
|
|
gtk_file_filter_add_pattern (filter, "*");
|
|
gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (chooser), filter);
|
|
|
|
filter = gtk_file_filter_new ();
|
|
gtk_file_filter_set_name (filter, _("Audio Files"));
|
|
gtk_file_filter_add_custom (filter, GTK_FILE_FILTER_MIME_TYPE, trd_audio_filter_func, NULL, NULL);
|
|
gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (chooser), filter);
|
|
|
|
filter = gtk_file_filter_new ();
|
|
gtk_file_filter_set_name (filter, _("Image Files"));
|
|
gtk_file_filter_add_pixbuf_formats (filter);
|
|
gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (chooser), filter);
|
|
|
|
filter = gtk_file_filter_new ();
|
|
gtk_file_filter_set_name (filter, _("Video Files"));
|
|
gtk_file_filter_add_custom (filter, GTK_FILE_FILTER_MIME_TYPE, trd_video_filter_func, NULL, NULL);
|
|
gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (chooser), filter);
|
|
|
|
/* check if "current-directory" is set */
|
|
if (G_LIKELY (renamer_dialog->current_directory != NULL))
|
|
{
|
|
/* use the "current-directory" as default for the chooser */
|
|
uri = thunarx_file_info_get_uri (THUNARX_FILE_INFO (renamer_dialog->current_directory));
|
|
gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER (chooser), uri);
|
|
g_free (uri);
|
|
}
|
|
|
|
/* run the chooser dialog */
|
|
if (gtk_dialog_run (GTK_DIALOG (chooser)) == GTK_RESPONSE_ACCEPT)
|
|
{
|
|
/* determine the URIs of the selected files */
|
|
uris = gtk_file_chooser_get_uris (GTK_FILE_CHOOSER (chooser));
|
|
for (lp = uris; lp != NULL; lp = lp->next)
|
|
{
|
|
/* determine the file for the URI */
|
|
file = thunar_file_get_for_uri (lp->data, NULL);
|
|
if (G_LIKELY (file != NULL))
|
|
{
|
|
/* append the file to the renamer model */
|
|
thunar_renamer_model_append (renamer_dialog->model, file);
|
|
|
|
/* unset sort order */
|
|
gtk_tree_view_column_set_sort_indicator (renamer_dialog->name_column, FALSE);
|
|
|
|
/* release the file */
|
|
g_object_unref (G_OBJECT (file));
|
|
}
|
|
|
|
/* release the URI */
|
|
g_free (lp->data);
|
|
}
|
|
g_slist_free (uris);
|
|
|
|
/* determine the current folder of the chooser */
|
|
uri = gtk_file_chooser_get_current_folder_uri (GTK_FILE_CHOOSER (chooser));
|
|
if (G_LIKELY (uri != NULL))
|
|
{
|
|
/* determine the file for that URI */
|
|
file = thunar_file_get_for_uri (uri, NULL);
|
|
if (G_LIKELY (file != NULL))
|
|
{
|
|
/* remember the current folder of the chooser as default */
|
|
thunar_renamer_dialog_set_current_directory (renamer_dialog, file);
|
|
|
|
/* release the file */
|
|
g_object_unref (G_OBJECT (file));
|
|
}
|
|
|
|
/* release the URI */
|
|
g_free (uri);
|
|
}
|
|
}
|
|
|
|
/* cleanup */
|
|
gtk_widget_destroy (chooser);
|
|
|
|
/* required in case of shortcut activation, in order to signal that the accel key got handled */
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
|
thunar_renamer_dialog_action_remove_files (ThunarRenamerDialog *renamer_dialog)
|
|
{
|
|
GtkTreeRowReference *row;
|
|
GtkTreeSelection *selection;
|
|
GtkTreeModel *model;
|
|
GtkTreePath *path;
|
|
GList *rows;
|
|
GList *lp;
|
|
|
|
_thunar_return_val_if_fail (THUNAR_IS_RENAMER_DIALOG (renamer_dialog), FALSE);
|
|
|
|
/* determine the selected rows in the view */
|
|
selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (renamer_dialog->tree_view));
|
|
rows = gtk_tree_selection_get_selected_rows (selection, &model);
|
|
for (lp = rows; lp != NULL; lp = lp->next)
|
|
{
|
|
/* turn the path into a tree row reference */
|
|
row = gtk_tree_row_reference_new (model, lp->data);
|
|
gtk_tree_path_free (lp->data);
|
|
lp->data = row;
|
|
}
|
|
|
|
/* actually remove the rows */
|
|
for (lp = rows; lp != NULL; lp = lp->next)
|
|
{
|
|
/* determine the path for the row */
|
|
path = gtk_tree_row_reference_get_path (lp->data);
|
|
if (G_LIKELY (path != NULL))
|
|
{
|
|
/* remove the row from the model */
|
|
thunar_renamer_model_remove (THUNAR_RENAMER_MODEL (model), path);
|
|
gtk_tree_path_free (path);
|
|
}
|
|
|
|
/* release the row reference */
|
|
gtk_tree_row_reference_free (lp->data);
|
|
}
|
|
|
|
/* cleanup */
|
|
g_list_free (rows);
|
|
|
|
/* required in case of shortcut activation, in order to signal that the accel key got handled */
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
|
thunar_renamer_dialog_action_clear (ThunarRenamerDialog *renamer_dialog)
|
|
{
|
|
_thunar_return_val_if_fail (THUNAR_IS_RENAMER_DIALOG (renamer_dialog), FALSE);
|
|
|
|
/* just clear the list of files in the model */
|
|
thunar_renamer_model_clear (renamer_dialog->model);
|
|
|
|
/* required in case of shortcut activation, in order to signal that the accel key got handled */
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
|
thunar_renamer_dialog_action_about (ThunarRenamerDialog *renamer_dialog)
|
|
{
|
|
_thunar_return_val_if_fail (THUNAR_IS_RENAMER_DIALOG (renamer_dialog), FALSE);
|
|
|
|
/* just popup the about dialog */
|
|
thunar_dialogs_show_about (GTK_WINDOW (renamer_dialog), _("Bulk Rename"), _("Thunar Bulk Rename is a powerful and extensible\n"
|
|
"tool to rename multiple files at once."));
|
|
|
|
/* required in case of shortcut activation, in order to signal that the accel key got handled */
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
thunar_renamer_dialog_name_column_clicked (GtkTreeViewColumn *column,
|
|
ThunarRenamerDialog *renamer_dialog)
|
|
{
|
|
GtkSortType sort_order = GTK_SORT_ASCENDING;
|
|
|
|
_thunar_return_if_fail (renamer_dialog->name_column == column);
|
|
|
|
if (!gtk_tree_view_column_get_sort_indicator (column))
|
|
{
|
|
/* show sort order */
|
|
gtk_tree_view_column_set_sort_indicator (column, TRUE);
|
|
}
|
|
else
|
|
{
|
|
/* invert new sort order */
|
|
if (gtk_tree_view_column_get_sort_order (column) == GTK_SORT_ASCENDING)
|
|
sort_order = GTK_SORT_DESCENDING;
|
|
else
|
|
sort_order = GTK_SORT_ASCENDING;
|
|
}
|
|
|
|
/* set new sort direction */
|
|
gtk_tree_view_column_set_sort_order (column, sort_order);
|
|
|
|
/* sort the model */
|
|
thunar_renamer_model_sort (renamer_dialog->model, sort_order);
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
|
thunar_renamer_dialog_button_press_event (GtkWidget *tree_view,
|
|
GdkEventButton *event,
|
|
ThunarRenamerDialog *renamer_dialog)
|
|
{
|
|
GtkTreeSelection *selection;
|
|
GtkTreePath *path;
|
|
|
|
_thunar_return_val_if_fail (THUNAR_IS_RENAMER_DIALOG (renamer_dialog), FALSE);
|
|
_thunar_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), FALSE);
|
|
|
|
/* determine the tree selection */
|
|
selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (tree_view));
|
|
|
|
/* we unselect all selected items if the user clicks on an empty
|
|
* area of the treeview and no modifier key is active.
|
|
*/
|
|
if ((event->state & gtk_accelerator_get_default_mod_mask ()) == 0
|
|
&& !gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (tree_view), event->x, event->y, NULL, NULL, NULL, NULL))
|
|
{
|
|
/* just unselect all rows */
|
|
gtk_tree_selection_unselect_all (selection);
|
|
}
|
|
|
|
/* check if we should popup the context menu */
|
|
if (event->window == gtk_tree_view_get_bin_window (GTK_TREE_VIEW (tree_view)) && event->type == GDK_BUTTON_PRESS && event->button == 3)
|
|
{
|
|
/* determine the path at the event coordinates */
|
|
if (gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (tree_view), event->x, event->y, &path, NULL, NULL, NULL))
|
|
{
|
|
/* select the path on which the user clicked if not already selected */
|
|
if (!gtk_tree_selection_path_is_selected (selection, path))
|
|
{
|
|
/* we don't unselect all other items if Control is active */
|
|
if ((event->state & GDK_CONTROL_MASK) == 0)
|
|
gtk_tree_selection_unselect_all (selection);
|
|
gtk_tree_selection_select_path (selection, path);
|
|
}
|
|
|
|
/* release the tree path */
|
|
gtk_tree_path_free (path);
|
|
}
|
|
|
|
/* popup the context menu */
|
|
thunar_renamer_dialog_context_menu (renamer_dialog);
|
|
|
|
/* we handled the event */
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
thunar_renamer_dialog_drag_data_received (GtkWidget *tree_view,
|
|
GdkDragContext *context,
|
|
gint x,
|
|
gint y,
|
|
GtkSelectionData *selection_data,
|
|
guint info,
|
|
guint timestamp,
|
|
ThunarRenamerDialog *renamer_dialog)
|
|
{
|
|
ThunarFile *file;
|
|
GList *file_list;
|
|
GList *lp;
|
|
GtkTreeModel *model;
|
|
GtkTreePath *path;
|
|
GtkTreeViewDropPosition drop_pos;
|
|
gint position = -1;
|
|
|
|
_thunar_return_if_fail (THUNAR_IS_RENAMER_DIALOG (renamer_dialog));
|
|
_thunar_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
|
|
|
|
/* we only accept text/uri-list drops with format 8 and atleast one byte of data */
|
|
if (info == TARGET_TEXT_URI_LIST && gtk_selection_data_get_format (selection_data) == 8 && gtk_selection_data_get_length (selection_data) > 0)
|
|
{
|
|
/* determine the renamer model */
|
|
model = gtk_tree_view_get_model (GTK_TREE_VIEW (tree_view));
|
|
|
|
/* get the drop position, if no path is foud we append the files */
|
|
if (gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (tree_view), x, y, &path, &drop_pos))
|
|
{
|
|
/* get the position of the path */
|
|
position = gtk_tree_path_get_indices (path)[0];
|
|
gtk_tree_path_free (path);
|
|
|
|
/* advance the offset if we drop after the item */
|
|
if (drop_pos == GTK_TREE_VIEW_DROP_AFTER || drop_pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER)
|
|
position++;
|
|
|
|
/* append if we've reached the end of the model */
|
|
if (position >= gtk_tree_model_iter_n_children (model, NULL))
|
|
position = -1;
|
|
}
|
|
|
|
/* determine the file list from the selection_data */
|
|
file_list = thunar_g_file_list_new_from_string ((const gchar *) gtk_selection_data_get_data (selection_data));
|
|
|
|
/* add all paths to the model */
|
|
for (lp = file_list; lp != NULL; lp = lp->next)
|
|
{
|
|
/* determine the file for the path */
|
|
file = thunar_file_get (lp->data, NULL);
|
|
if (G_LIKELY (file != NULL))
|
|
{
|
|
/* insert the file in the model */
|
|
thunar_renamer_model_insert (renamer_dialog->model, file, position);
|
|
|
|
/* unset sort order */
|
|
gtk_tree_view_column_set_sort_indicator (renamer_dialog->name_column, FALSE);
|
|
|
|
/* advance the offset if we do not append, so the drop order is consistent */
|
|
if (position != -1)
|
|
position++;
|
|
|
|
/* release the file */
|
|
g_object_unref (file);
|
|
}
|
|
|
|
/* release the GFile */
|
|
g_object_unref (lp->data);
|
|
}
|
|
|
|
/* finish the drag */
|
|
gtk_drag_finish (context, (file_list != NULL), FALSE, timestamp);
|
|
|
|
/* release the list */
|
|
g_list_free (file_list);
|
|
}
|
|
|
|
/* stop the emission of the "drag-data-received" signal */
|
|
g_signal_stop_emission_by_name (G_OBJECT (tree_view), "drag-data-received");
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
thunar_renamer_dialog_drag_leave (GtkWidget *tree_view,
|
|
GdkDragContext *context,
|
|
guint timestamp,
|
|
ThunarRenamerDialog *renamer_dialog)
|
|
{
|
|
_thunar_return_if_fail (THUNAR_IS_RENAMER_DIALOG (renamer_dialog));
|
|
_thunar_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
|
|
|
|
/* disable the highlighting */
|
|
if (renamer_dialog->drag_highlighted)
|
|
{
|
|
/* we use the tree view parent (the scrolled window),
|
|
* as the drag_highlight doesn't work for tree views.
|
|
*/
|
|
gtk_drag_unhighlight (gtk_widget_get_parent (tree_view));
|
|
renamer_dialog->drag_highlighted = FALSE;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
|
thunar_renamer_dialog_drag_motion (GtkWidget *tree_view,
|
|
GdkDragContext *context,
|
|
gint x,
|
|
gint y,
|
|
guint timestamp,
|
|
ThunarRenamerDialog *renamer_dialog)
|
|
{
|
|
GdkAtom target;
|
|
GtkTreeViewDropPosition position;
|
|
GtkTreePath *path;
|
|
GdkDragAction action;
|
|
GtkTreeModel *model;
|
|
gint n_rows;
|
|
|
|
_thunar_return_val_if_fail (THUNAR_IS_RENAMER_DIALOG (renamer_dialog), FALSE);
|
|
_thunar_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), FALSE);
|
|
|
|
/* determine the drop target */
|
|
target = gtk_drag_dest_find_target (tree_view, context, NULL);
|
|
if (target == gdk_atom_intern_static_string ("text/uri-list"))
|
|
{
|
|
/* set the drag action for uris */
|
|
action = GDK_ACTION_COPY;
|
|
}
|
|
else if (target == gdk_atom_intern_static_string ("GTK_TREE_MODEL_ROW"))
|
|
{
|
|
/* set the drag action for tree rows */
|
|
action = GDK_ACTION_MOVE;
|
|
}
|
|
else
|
|
{
|
|
if (renamer_dialog->drag_highlighted)
|
|
{
|
|
/* we use the tree view parent (the scrolled window),
|
|
* as the drag_highlight doesn't work for tree views.
|
|
*/
|
|
gtk_drag_unhighlight (gtk_widget_get_parent (tree_view));
|
|
renamer_dialog->drag_highlighted = FALSE;
|
|
}
|
|
|
|
/* we cannot handle the drop */
|
|
return FALSE;
|
|
}
|
|
|
|
/* compute the drop position */
|
|
if (gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (tree_view), x, y, &path, &position))
|
|
{
|
|
/* we can only drop before or after an item */
|
|
if (position == GTK_TREE_VIEW_DROP_BEFORE || position == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)
|
|
position = GTK_TREE_VIEW_DROP_BEFORE;
|
|
else
|
|
position = GTK_TREE_VIEW_DROP_AFTER;
|
|
}
|
|
else
|
|
{
|
|
/* append the item if there are rows in the model */
|
|
model = gtk_tree_view_get_model (GTK_TREE_VIEW (tree_view));
|
|
n_rows = gtk_tree_model_iter_n_children (model, NULL);
|
|
|
|
if (G_LIKELY (n_rows > 0))
|
|
{
|
|
/* get the last tree path in the model */
|
|
path = gtk_tree_path_new_from_indices (n_rows - 1, -1);
|
|
position = GTK_TREE_VIEW_DROP_AFTER;
|
|
}
|
|
}
|
|
|
|
if (G_LIKELY (path != NULL))
|
|
{
|
|
/* highlight the appropriate row */
|
|
gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (tree_view), path, position);
|
|
gtk_tree_path_free (path);
|
|
}
|
|
else if (!renamer_dialog->drag_highlighted)
|
|
{
|
|
/* highlight the parent */
|
|
gtk_drag_highlight (gtk_widget_get_parent (tree_view));
|
|
renamer_dialog->drag_highlighted = TRUE;
|
|
}
|
|
|
|
/* we can handle the drop */
|
|
gdk_drag_status (context, action, timestamp);
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
|
thunar_renamer_dialog_drag_drop (GtkWidget *tree_view,
|
|
GdkDragContext *context,
|
|
gint x,
|
|
gint y,
|
|
guint timestamp,
|
|
ThunarRenamerDialog *renamer_dialog)
|
|
{
|
|
GdkAtom target;
|
|
GtkTreeSelection *selection;
|
|
GtkTreePath *dst_path;
|
|
GtkTreeModel *model;
|
|
GtkTreeViewDropPosition drop_pos;
|
|
gint position = -1;
|
|
GList *rows;
|
|
|
|
_thunar_return_val_if_fail (THUNAR_IS_RENAMER_DIALOG (renamer_dialog), FALSE);
|
|
_thunar_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), FALSE);
|
|
|
|
/* determine the drop target */
|
|
target = gtk_drag_dest_find_target (tree_view, context, NULL);
|
|
if (G_LIKELY (target == gdk_atom_intern_static_string ("text/uri-list")))
|
|
{
|
|
/* request the data, we call gtk_drag_finish() later */
|
|
gtk_drag_get_data (tree_view, context, target, timestamp);
|
|
}
|
|
else if (target == gdk_atom_intern_static_string ("GTK_TREE_MODEL_ROW"))
|
|
{
|
|
selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (tree_view));
|
|
rows = gtk_tree_selection_get_selected_rows (selection, &model);
|
|
if (G_LIKELY (rows != NULL))
|
|
{
|
|
/* get the drop position, if no path is found we append the file */
|
|
if (gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (tree_view), x, y, &dst_path, &drop_pos))
|
|
{
|
|
/* get the position of the path */
|
|
position = gtk_tree_path_get_indices (dst_path)[0];
|
|
gtk_tree_path_free (dst_path);
|
|
|
|
/* advance the offset if we drop after the item */
|
|
if (drop_pos == GTK_TREE_VIEW_DROP_AFTER || drop_pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER)
|
|
position++;
|
|
}
|
|
|
|
/* perform the move */
|
|
if (G_LIKELY (rows != NULL))
|
|
{
|
|
thunar_renamer_model_reorder (renamer_dialog->model, rows, position);
|
|
g_list_free_full (rows, (GDestroyNotify) gtk_tree_path_free);
|
|
}
|
|
|
|
/* unset sort column */
|
|
gtk_tree_view_column_set_sort_indicator (renamer_dialog->name_column, FALSE);
|
|
}
|
|
|
|
/* finish the dnd operation */
|
|
gtk_drag_finish (context, TRUE, FALSE, timestamp);
|
|
}
|
|
else
|
|
{
|
|
/* we cannot handle the drop */
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
thunar_renamer_dialog_notify_page (GtkNotebook *notebook,
|
|
GParamSpec *pspec,
|
|
ThunarRenamerDialog *renamer_dialog)
|
|
{
|
|
GtkWidget *renamer;
|
|
gint page;
|
|
|
|
_thunar_return_if_fail (THUNAR_IS_RENAMER_DIALOG (renamer_dialog));
|
|
_thunar_return_if_fail (GTK_IS_NOTEBOOK (notebook));
|
|
|
|
/* determine the currently active renamer page */
|
|
page = gtk_notebook_get_current_page (notebook);
|
|
if (G_LIKELY (page >= 0))
|
|
{
|
|
/* determine the renamer for that page... */
|
|
renamer = gtk_notebook_get_nth_page (notebook, page);
|
|
|
|
/* ...and set that renamer for the model */
|
|
thunar_renamer_model_set_renamer (renamer_dialog->model, THUNARX_RENAMER (renamer));
|
|
}
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
|
thunar_renamer_dialog_popup_menu (GtkWidget *tree_view,
|
|
ThunarRenamerDialog *renamer_dialog)
|
|
{
|
|
_thunar_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), FALSE);
|
|
_thunar_return_val_if_fail (THUNAR_IS_RENAMER_DIALOG (renamer_dialog), FALSE);
|
|
|
|
/* popup the context menu */
|
|
thunar_renamer_dialog_context_menu (renamer_dialog);
|
|
|
|
/* we handled the event */
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
thunar_renamer_dialog_row_activated (GtkTreeView *tree_view,
|
|
GtkTreePath *path,
|
|
GtkTreeViewColumn *column,
|
|
ThunarRenamerDialog *renamer_dialog)
|
|
{
|
|
_thunar_return_if_fail (THUNAR_IS_RENAMER_DIALOG (renamer_dialog));
|
|
|
|
thunar_action_manager_activate_selected_files (renamer_dialog->action_mgr, THUNAR_ACTION_MANAGER_OPEN_AS_NEW_WINDOW, NULL);
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
thunar_renamer_dialog_selection_changed (GtkTreeSelection *selection,
|
|
ThunarRenamerDialog *renamer_dialog)
|
|
{
|
|
GtkTreeModel *model;
|
|
GtkTreeIter iter;
|
|
ThunarFile *file;
|
|
GList *rows;
|
|
GList *lp;
|
|
gint n_selected_files = 0;
|
|
|
|
_thunar_return_if_fail (GTK_IS_TREE_SELECTION (selection));
|
|
_thunar_return_if_fail (THUNAR_IS_RENAMER_DIALOG (renamer_dialog));
|
|
|
|
/* release the list of previously selected files */
|
|
thunarx_file_info_list_free (renamer_dialog->selected_files);
|
|
renamer_dialog->selected_files = NULL;
|
|
|
|
/* determine the new list of selected files */
|
|
rows = gtk_tree_selection_get_selected_rows (selection, &model);
|
|
for (lp = g_list_last (rows); lp != NULL; lp = lp->prev, ++n_selected_files)
|
|
{
|
|
/* determine the iter for this path */
|
|
if (gtk_tree_model_get_iter (model, &iter, lp->data))
|
|
{
|
|
/* determine the file for this row and prepend it to our list */
|
|
gtk_tree_model_get (model, &iter, THUNAR_RENAMER_MODEL_COLUMN_FILE, &file, -1);
|
|
if (G_LIKELY (file != NULL))
|
|
renamer_dialog->selected_files = g_list_prepend (renamer_dialog->selected_files, file);
|
|
}
|
|
|
|
/* release this path */
|
|
gtk_tree_path_free (lp->data);
|
|
}
|
|
g_list_free (rows);
|
|
|
|
/* notify listeners */
|
|
g_object_notify (G_OBJECT (renamer_dialog), "selected-files");
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* thunar_renamer_dialog_get_current_directory:
|
|
* @renamer_dialog : a #ThunarRenamerDialog.
|
|
*
|
|
* Returns the current directory of the @renamer_dialog, which
|
|
* is used as default directory for the "Add Files" dialog.
|
|
* May also be %NULL in which case the current directory of the
|
|
* file manager process will be used.
|
|
*
|
|
* Return value: the current directory for @renamer_dialog.
|
|
**/
|
|
static ThunarFile *
|
|
thunar_renamer_dialog_get_current_directory (ThunarRenamerDialog *renamer_dialog)
|
|
{
|
|
_thunar_return_val_if_fail (THUNAR_IS_RENAMER_DIALOG (renamer_dialog), NULL);
|
|
return renamer_dialog->current_directory;
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* thunar_renamer_dialog_set_current_directory:
|
|
* @renamer_dialog : a #ThunarRenamerDialog.
|
|
* @current_directory : a #ThunarFile or %NULL.
|
|
*
|
|
* The @renamer_dialog will use @current_directory as default
|
|
* directory for the "Add Files" dialog. If @current_directory
|
|
* is %NULL, the current directory of the file manager process
|
|
* will be used.
|
|
**/
|
|
static void
|
|
thunar_renamer_dialog_set_current_directory (ThunarRenamerDialog *renamer_dialog,
|
|
ThunarFile *current_directory)
|
|
{
|
|
_thunar_return_if_fail (THUNAR_IS_RENAMER_DIALOG (renamer_dialog));
|
|
_thunar_return_if_fail (current_directory == NULL || THUNAR_IS_FILE (current_directory));
|
|
|
|
/* check if we already use that directory */
|
|
if (G_UNLIKELY (renamer_dialog->current_directory == current_directory))
|
|
return;
|
|
|
|
/* disconnect from the previous directory */
|
|
if (renamer_dialog->current_directory != NULL)
|
|
g_object_unref (G_OBJECT (renamer_dialog->current_directory));
|
|
|
|
/* activate the new directory */
|
|
renamer_dialog->current_directory = current_directory;
|
|
|
|
/* connect to the new directory */
|
|
if (current_directory != NULL)
|
|
g_object_ref (G_OBJECT (current_directory));
|
|
|
|
/* notify listeners */
|
|
g_object_notify (G_OBJECT (renamer_dialog), "current-directory");
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* thunar_renamer_dialog_get_selected_files:
|
|
* @renamer_dialog : a #ThunarRenamerDialog.
|
|
*
|
|
* Returns the #GList of the currently selected #ThunarFile<!---->s
|
|
* in @renamer_dialog.
|
|
*
|
|
* Return value: the list of currently selected files.
|
|
**/
|
|
static GList *
|
|
thunar_renamer_dialog_get_selected_files (ThunarRenamerDialog *renamer_dialog)
|
|
{
|
|
_thunar_return_val_if_fail (THUNAR_IS_RENAMER_DIALOG (renamer_dialog), NULL);
|
|
return renamer_dialog->selected_files;
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* thunar_renamer_dialog_get_standalone:
|
|
* @renamer_dialog : a #ThunarRenamerDialog.
|
|
*
|
|
* Returns %TRUE if the @renamer_dialog is standalone, i.e.
|
|
* if it was not opened from within the file manager with
|
|
* a preset file list.
|
|
*
|
|
* Return value: %TRUE if @renamer_dialog is standalone.
|
|
**/
|
|
static gboolean
|
|
thunar_renamer_dialog_get_standalone (ThunarRenamerDialog *renamer_dialog)
|
|
{
|
|
_thunar_return_val_if_fail (THUNAR_IS_RENAMER_DIALOG (renamer_dialog), FALSE);
|
|
return renamer_dialog->standalone;
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* thunar_renamer_dialog_set_standalone:
|
|
* @renamer_dialog : a #ThunarRenamerDialog.
|
|
* @standalone : the new standalone state for @renamer_dialog.
|
|
*
|
|
* If @standalone is %TRUE the @renamer_dialog will display a
|
|
* toolbar and several other actions that do not make sense
|
|
* when opened from within the file manager.
|
|
**/
|
|
static void
|
|
thunar_renamer_dialog_set_standalone (ThunarRenamerDialog *renamer_dialog,
|
|
gboolean standalone)
|
|
{
|
|
_thunar_return_if_fail (THUNAR_IS_RENAMER_DIALOG (renamer_dialog));
|
|
|
|
/* normalize the value */
|
|
standalone = !!standalone;
|
|
|
|
/* check if we have a new setting */
|
|
if (G_LIKELY (renamer_dialog->standalone != standalone))
|
|
{
|
|
/* apply the new setting */
|
|
renamer_dialog->standalone = standalone;
|
|
|
|
/* change title to reflect the standalone status */
|
|
gtk_window_set_title (GTK_WINDOW (renamer_dialog), standalone ? _("Bulk Rename - Rename Multiple Files") : _("Rename Multiple Files"));
|
|
|
|
/* notify listeners */
|
|
g_object_notify (G_OBJECT (renamer_dialog), "standalone");
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* thunar_show_renamer_dialog:
|
|
* @parent : the #GtkWidget or the #GdkScreen on which to open the
|
|
* dialog. May also be %NULL in which case the default
|
|
* #GdkScreen will be used.
|
|
* @current_directory : the current directory for the #ThunarRenamerDialog,
|
|
* which is used as default folder for the "Add Files"
|
|
* dialog. May also be %NULL in which case the current
|
|
* directory of the file manager process will be used.
|
|
* @files : the list of #ThunarFile<!---->s to rename.
|
|
* @standalone : whether the dialog should appear like a standalone
|
|
* application instead of an integrated renamer dialog.
|
|
* @startup_id : startup id to set on the window or %NULL.
|
|
*
|
|
* Convenience function to display a #ThunarRenamerDialog with
|
|
* the given parameters.
|
|
**/
|
|
void
|
|
thunar_show_renamer_dialog (gpointer parent,
|
|
ThunarFile *current_directory,
|
|
GList *files,
|
|
gboolean standalone,
|
|
const gchar *startup_id)
|
|
{
|
|
ThunarApplication *application;
|
|
GdkScreen *screen;
|
|
GtkWidget *dialog;
|
|
GtkWidget *window = NULL;
|
|
GtkComboBox *mode_combo;
|
|
GList *lp;
|
|
gboolean directories_only;
|
|
|
|
_thunar_return_if_fail (parent == NULL || GDK_IS_SCREEN (parent) || GTK_IS_WIDGET (parent));
|
|
|
|
/* determine the screen for the dialog */
|
|
if (G_UNLIKELY (parent == NULL))
|
|
{
|
|
/* just use the default screen, no toplevel window */
|
|
screen = gdk_screen_get_default ();
|
|
}
|
|
else if (GTK_IS_WIDGET (parent))
|
|
{
|
|
/* use the screen for the widget and the toplevel window */
|
|
screen = gtk_widget_get_screen (parent);
|
|
window = gtk_widget_get_toplevel (parent);
|
|
}
|
|
else
|
|
{
|
|
/* parent is a screen, no toplevel window */
|
|
screen = GDK_SCREEN (parent);
|
|
}
|
|
|
|
/* display the bulk rename dialog */
|
|
dialog = g_object_new (THUNAR_TYPE_RENAMER_DIALOG,
|
|
"current-directory", current_directory,
|
|
"screen", screen,
|
|
"standalone", standalone,
|
|
NULL);
|
|
|
|
/* set the dialogs startup id if available */
|
|
if (startup_id != NULL && *startup_id != '\0')
|
|
gtk_window_set_startup_id (GTK_WINDOW (dialog), startup_id);
|
|
|
|
/* check if we have a toplevel window */
|
|
if (G_LIKELY (window != NULL && gtk_widget_get_toplevel (window)))
|
|
{
|
|
/* dialog is transient for toplevel window, but not modal */
|
|
gtk_window_set_destroy_with_parent (GTK_WINDOW (dialog), TRUE);
|
|
gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (window));
|
|
}
|
|
|
|
/* append all specified files to the dialog's model */
|
|
for (lp = files; lp != NULL; lp = lp->next)
|
|
thunar_renamer_model_append (THUNAR_RENAMER_DIALOG (dialog)->model, lp->data);
|
|
|
|
/* if there are only directories selected change the mode to both */
|
|
directories_only = g_list_length (files) == 0 ? FALSE : TRUE;
|
|
|
|
for (lp = files; lp != NULL; lp = lp->next)
|
|
if (thunar_file_is_directory (THUNAR_FILE (lp->data)) == FALSE)
|
|
directories_only = FALSE;
|
|
|
|
mode_combo = GTK_COMBO_BOX (THUNAR_RENAMER_DIALOG (dialog)->mode_combo);
|
|
if (directories_only && mode_combo != NULL)
|
|
gtk_combo_box_set_active (mode_combo, THUNAR_RENAMER_MODE_BOTH);
|
|
|
|
/* let the application handle the dialog */
|
|
application = thunar_application_get ();
|
|
thunar_application_take_window (application, GTK_WINDOW (dialog));
|
|
g_object_unref (G_OBJECT (application));
|
|
|
|
/* display the dialog */
|
|
gtk_widget_show (dialog);
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
|
thunar_renamer_configure_event (GtkWidget *widget,
|
|
GdkEventConfigure *event)
|
|
{
|
|
ThunarRenamerDialog *dialog = THUNAR_RENAMER_DIALOG (widget);
|
|
GtkAllocation widget_allocation;
|
|
|
|
gtk_widget_get_allocation (widget, &widget_allocation);
|
|
|
|
/* check if we have a new dimension here */
|
|
if (widget_allocation.width != event->width || widget_allocation.height != event->height)
|
|
{
|
|
/* drop any previous timer source */
|
|
if (dialog->save_geometry_timer_id != 0)
|
|
g_source_remove (dialog->save_geometry_timer_id);
|
|
|
|
/* check if we should schedule another save timer */
|
|
if (gtk_widget_get_visible (widget))
|
|
{
|
|
/* save the geometry one second after the last configure event */
|
|
dialog->save_geometry_timer_id = g_timeout_add_seconds_full (G_PRIORITY_LOW, 1, thunar_util_save_geometry_timer,
|
|
dialog, thunar_renamer_save_geometry_timer_destroy);
|
|
}
|
|
}
|
|
|
|
/* let Gtk+ handle the configure event */
|
|
return (*GTK_WIDGET_CLASS (thunar_renamer_dialog_parent_class)->configure_event) (widget, event);
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
thunar_renamer_save_geometry_timer_destroy (gpointer user_data)
|
|
{
|
|
THUNAR_RENAMER_DIALOG (user_data)->save_geometry_timer_id = 0;
|
|
}
|