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.
1542 lines
46 KiB
C
1542 lines
46 KiB
C
/* vi:set et ai sw=2 sts=2 ts=2: */
|
|
/*-
|
|
* Copyright (c) 2005-2006 Benedikt Meurer <benny@xfce.org>
|
|
* Copyright (c) 2009 Jannis Pohlmann <jannis@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-gobject-extensions.h"
|
|
#include "thunar/thunar-private.h"
|
|
#include "thunar/thunar-renamer-model.h"
|
|
#include "thunar/thunar-util.h"
|
|
|
|
|
|
|
|
#define THUNAR_RENAMER_MODEL_ITEM(item) ((ThunarRenamerModelItem *) (item))
|
|
|
|
|
|
|
|
/* Property identifiers */
|
|
enum
|
|
{
|
|
PROP_0,
|
|
PROP_CAN_RENAME,
|
|
PROP_FROZEN,
|
|
PROP_MODE,
|
|
PROP_RENAMER,
|
|
};
|
|
|
|
typedef struct
|
|
{
|
|
gint offset;
|
|
gint position;
|
|
GList *item;
|
|
} SortTuple;
|
|
|
|
|
|
|
|
typedef struct _ThunarRenamerModelItem ThunarRenamerModelItem;
|
|
|
|
|
|
|
|
static void
|
|
thunar_renamer_model_tree_model_init (GtkTreeModelIface *iface);
|
|
static void
|
|
thunar_renamer_model_finalize (GObject *object);
|
|
static void
|
|
thunar_renamer_model_get_property (GObject *object,
|
|
guint prop_id,
|
|
GValue *value,
|
|
GParamSpec *pspec);
|
|
static void
|
|
thunar_renamer_model_set_property (GObject *object,
|
|
guint prop_id,
|
|
const GValue *value,
|
|
GParamSpec *pspec);
|
|
static GtkTreeModelFlags
|
|
thunar_renamer_model_get_flags (GtkTreeModel *tree_model);
|
|
static gint
|
|
thunar_renamer_model_get_n_columns (GtkTreeModel *tree_model);
|
|
static GType
|
|
thunar_renamer_model_get_column_type (GtkTreeModel *tree_model,
|
|
gint column);
|
|
static gboolean
|
|
thunar_renamer_model_get_iter (GtkTreeModel *tree_model,
|
|
GtkTreeIter *iter,
|
|
GtkTreePath *path);
|
|
static GtkTreePath *
|
|
thunar_renamer_model_get_path (GtkTreeModel *tree_model,
|
|
GtkTreeIter *iter);
|
|
static void
|
|
thunar_renamer_model_get_value (GtkTreeModel *tree_model,
|
|
GtkTreeIter *iter,
|
|
gint column,
|
|
GValue *value);
|
|
static gboolean
|
|
thunar_renamer_model_iter_next (GtkTreeModel *tree_model,
|
|
GtkTreeIter *iter);
|
|
static gboolean
|
|
thunar_renamer_model_iter_children (GtkTreeModel *tree_model,
|
|
GtkTreeIter *iter,
|
|
GtkTreeIter *parent);
|
|
static gboolean
|
|
thunar_renamer_model_iter_has_child (GtkTreeModel *tree_model,
|
|
GtkTreeIter *iter);
|
|
static gint
|
|
thunar_renamer_model_iter_n_children (GtkTreeModel *tree_model,
|
|
GtkTreeIter *iter);
|
|
static gboolean
|
|
thunar_renamer_model_iter_nth_child (GtkTreeModel *tree_model,
|
|
GtkTreeIter *iter,
|
|
GtkTreeIter *parent,
|
|
gint n);
|
|
static gboolean
|
|
thunar_renamer_model_iter_parent (GtkTreeModel *tree_model,
|
|
GtkTreeIter *iter,
|
|
GtkTreeIter *child);
|
|
static void
|
|
thunar_renamer_model_file_changed (ThunarRenamerModel *renamer_model,
|
|
ThunarFile *file);
|
|
static void
|
|
thunar_renamer_model_file_destroyed (ThunarRenamerModel *renamer_model,
|
|
ThunarFile *file);
|
|
static void
|
|
thunar_renamer_model_invalidate_all (ThunarRenamerModel *renamer_model);
|
|
static void
|
|
thunar_renamer_model_invalidate_item (ThunarRenamerModel *renamer_model,
|
|
ThunarRenamerModelItem *item);
|
|
static gboolean
|
|
thunar_renamer_model_conflict_item (ThunarRenamerModel *renamer_model,
|
|
ThunarRenamerModelItem *item);
|
|
static gchar *
|
|
thunar_renamer_model_process_item (ThunarRenamerModel *renamer_model,
|
|
ThunarRenamerModelItem *item,
|
|
guint idx);
|
|
static gboolean
|
|
thunar_renamer_model_update_idle (gpointer user_data);
|
|
static void
|
|
thunar_renamer_model_update_idle_destroy (gpointer user_data);
|
|
static ThunarRenamerModelItem *
|
|
thunar_renamer_model_item_new (ThunarRenamerModel *renamer_model,
|
|
ThunarFile *file) G_GNUC_MALLOC;
|
|
static void
|
|
thunar_renamer_model_release_item (ThunarRenamerModel *renamer_model,
|
|
ThunarRenamerModelItem *item);
|
|
static gint
|
|
thunar_renamer_model_cmp_array (gconstpointer pointer_a,
|
|
gconstpointer pointer_b,
|
|
gpointer user_data);
|
|
static gboolean
|
|
thunar_renamer_model_get_can_rename (ThunarRenamerModel *renamer_model);
|
|
static gboolean
|
|
thunar_renamer_model_get_frozen (ThunarRenamerModel *renamer_model);
|
|
static void
|
|
thunar_renamer_model_set_frozen (ThunarRenamerModel *renamer_model,
|
|
gboolean frozen);
|
|
static void
|
|
thunar_renamer_model_set_mode (ThunarRenamerModel *renamer_model,
|
|
ThunarRenamerMode mode);
|
|
|
|
|
|
|
|
struct _ThunarRenamerModelClass
|
|
{
|
|
GObjectClass __parent__;
|
|
};
|
|
|
|
struct _ThunarRenamerModel
|
|
{
|
|
GObject __parent__;
|
|
|
|
/* the model stamp is only used when debugging is
|
|
* enabled, to make sure we don't accept iterators
|
|
* generated by another model.
|
|
*/
|
|
#ifndef NDEBUG
|
|
gint stamp;
|
|
#endif
|
|
|
|
ThunarRenamerMode mode;
|
|
ThunarxRenamer *renamer;
|
|
GList *items;
|
|
|
|
/* TRUE if the model is currently frozen */
|
|
gboolean frozen;
|
|
|
|
/* the idle source used to update the model */
|
|
guint update_idle_id;
|
|
};
|
|
|
|
struct _ThunarRenamerModelItem
|
|
{
|
|
ThunarFile *file;
|
|
gchar *name;
|
|
guint64 date_changed;
|
|
guint changed : 1; /* if the file changed */
|
|
guint conflict : 1; /* if the item conflicts with another item */
|
|
guint dirty : 1; /* if the item must be updated */
|
|
};
|
|
|
|
|
|
|
|
G_DEFINE_TYPE_WITH_CODE (ThunarRenamerModel, thunar_renamer_model, G_TYPE_OBJECT,
|
|
G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_MODEL, thunar_renamer_model_tree_model_init))
|
|
|
|
|
|
|
|
static void
|
|
thunar_renamer_model_class_init (ThunarRenamerModelClass *klass)
|
|
{
|
|
GObjectClass *gobject_class;
|
|
|
|
gobject_class = G_OBJECT_CLASS (klass);
|
|
gobject_class->finalize = thunar_renamer_model_finalize;
|
|
gobject_class->get_property = thunar_renamer_model_get_property;
|
|
gobject_class->set_property = thunar_renamer_model_set_property;
|
|
|
|
/**
|
|
* ThunarRenamerModel:can-rename:
|
|
*
|
|
* Whether the contents of the #ThunarRenamerModel
|
|
* can be renamed. This is %TRUE if atleast one file
|
|
* is in the model and no conflict is present.
|
|
**/
|
|
g_object_class_install_property (gobject_class,
|
|
PROP_CAN_RENAME,
|
|
g_param_spec_boolean ("can-rename",
|
|
"can-rename",
|
|
"can-rename",
|
|
FALSE,
|
|
EXO_PARAM_READABLE));
|
|
|
|
/**
|
|
* ThunarRenamerModel:frozen:
|
|
*
|
|
* If a #ThunarRenamerModel is frozen, no updates will
|
|
* be processed for the model and the "can-rename"
|
|
* property will always be %FALSE.
|
|
**/
|
|
g_object_class_install_property (gobject_class,
|
|
PROP_FROZEN,
|
|
g_param_spec_boolean ("frozen",
|
|
"frozen",
|
|
"frozen",
|
|
FALSE,
|
|
EXO_PARAM_READWRITE));
|
|
|
|
/**
|
|
* ThunarRenamerModel:mode:
|
|
*
|
|
* The #ThunarRenamerMode used by this
|
|
* #ThunarRenamerModel.
|
|
**/
|
|
g_object_class_install_property (gobject_class,
|
|
PROP_MODE,
|
|
g_param_spec_enum ("mode", "mode", "mode",
|
|
THUNAR_TYPE_RENAMER_MODE,
|
|
THUNAR_RENAMER_MODE_NAME,
|
|
EXO_PARAM_READWRITE));
|
|
|
|
/**
|
|
* ThunarRenamerModel:renamer:
|
|
*
|
|
* The #ThunarxRenamer that should be used by this
|
|
* #ThunarRenamerModel.
|
|
**/
|
|
g_object_class_install_property (gobject_class,
|
|
PROP_RENAMER,
|
|
g_param_spec_object ("renamer",
|
|
"renamer",
|
|
"renamer",
|
|
THUNARX_TYPE_RENAMER,
|
|
EXO_PARAM_READWRITE));
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
thunar_renamer_model_tree_model_init (GtkTreeModelIface *iface)
|
|
{
|
|
iface->get_flags = thunar_renamer_model_get_flags;
|
|
iface->get_n_columns = thunar_renamer_model_get_n_columns;
|
|
iface->get_column_type = thunar_renamer_model_get_column_type;
|
|
iface->get_iter = thunar_renamer_model_get_iter;
|
|
iface->get_path = thunar_renamer_model_get_path;
|
|
iface->get_value = thunar_renamer_model_get_value;
|
|
iface->iter_next = thunar_renamer_model_iter_next;
|
|
iface->iter_children = thunar_renamer_model_iter_children;
|
|
iface->iter_has_child = thunar_renamer_model_iter_has_child;
|
|
iface->iter_n_children = thunar_renamer_model_iter_n_children;
|
|
iface->iter_nth_child = thunar_renamer_model_iter_nth_child;
|
|
iface->iter_parent = thunar_renamer_model_iter_parent;
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
thunar_renamer_model_init (ThunarRenamerModel *renamer_model)
|
|
{
|
|
/* generate a unique if we're in debug mode */
|
|
#ifndef NDEBUG
|
|
renamer_model->stamp = g_random_int ();
|
|
#endif
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
thunar_renamer_model_finalize (GObject *object)
|
|
{
|
|
GList *lp;
|
|
ThunarRenamerModel *renamer_model = THUNAR_RENAMER_MODEL (object);
|
|
|
|
/* reset the renamer property (must be first!) */
|
|
thunar_renamer_model_set_renamer (renamer_model, NULL);
|
|
|
|
/* release all items */
|
|
for (lp = renamer_model->items; lp != NULL; lp = lp->next)
|
|
thunar_renamer_model_release_item (renamer_model, lp->data);
|
|
|
|
g_list_free (renamer_model->items);
|
|
|
|
/* be sure to cancel any pending update idle source (must be last!) */
|
|
if (G_UNLIKELY (renamer_model->update_idle_id != 0))
|
|
g_source_remove (renamer_model->update_idle_id);
|
|
|
|
(*G_OBJECT_CLASS (thunar_renamer_model_parent_class)->finalize) (object);
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
thunar_renamer_model_get_property (GObject *object,
|
|
guint prop_id,
|
|
GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
ThunarRenamerModel *renamer_model = THUNAR_RENAMER_MODEL (object);
|
|
|
|
switch (prop_id)
|
|
{
|
|
case PROP_CAN_RENAME:
|
|
g_value_set_boolean (value, thunar_renamer_model_get_can_rename (renamer_model));
|
|
break;
|
|
|
|
case PROP_FROZEN:
|
|
g_value_set_boolean (value, thunar_renamer_model_get_frozen (renamer_model));
|
|
break;
|
|
|
|
case PROP_MODE:
|
|
g_value_set_enum (value, thunar_renamer_model_get_mode (renamer_model));
|
|
break;
|
|
|
|
case PROP_RENAMER:
|
|
g_value_set_object (value, thunar_renamer_model_get_renamer (renamer_model));
|
|
break;
|
|
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
thunar_renamer_model_set_property (GObject *object,
|
|
guint prop_id,
|
|
const GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
ThunarRenamerModel *renamer_model = THUNAR_RENAMER_MODEL (object);
|
|
|
|
switch (prop_id)
|
|
{
|
|
case PROP_FROZEN:
|
|
thunar_renamer_model_set_frozen (renamer_model, g_value_get_boolean (value));
|
|
break;
|
|
|
|
case PROP_MODE:
|
|
thunar_renamer_model_set_mode (renamer_model, g_value_get_enum (value));
|
|
break;
|
|
|
|
case PROP_RENAMER:
|
|
thunar_renamer_model_set_renamer (renamer_model, g_value_get_object (value));
|
|
break;
|
|
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
static GtkTreeModelFlags
|
|
thunar_renamer_model_get_flags (GtkTreeModel *tree_model)
|
|
{
|
|
return GTK_TREE_MODEL_ITERS_PERSIST | GTK_TREE_MODEL_LIST_ONLY;
|
|
}
|
|
|
|
|
|
|
|
static gint
|
|
thunar_renamer_model_get_n_columns (GtkTreeModel *tree_model)
|
|
{
|
|
return THUNAR_RENAMER_MODEL_N_COLUMNS;
|
|
}
|
|
|
|
|
|
|
|
static GType
|
|
thunar_renamer_model_get_column_type (GtkTreeModel *tree_model,
|
|
gint column)
|
|
{
|
|
switch (column)
|
|
{
|
|
case THUNAR_RENAMER_MODEL_COLUMN_CONFLICT:
|
|
return G_TYPE_BOOLEAN;
|
|
|
|
case THUNAR_RENAMER_MODEL_COLUMN_CONFLICT_WEIGHT:
|
|
return PANGO_TYPE_WEIGHT;
|
|
|
|
case THUNAR_RENAMER_MODEL_COLUMN_FILE:
|
|
return THUNAR_TYPE_FILE;
|
|
|
|
case THUNAR_RENAMER_MODEL_COLUMN_NEWNAME:
|
|
return G_TYPE_STRING;
|
|
|
|
case THUNAR_RENAMER_MODEL_COLUMN_OLDNAME:
|
|
return G_TYPE_STRING;
|
|
|
|
default:
|
|
_thunar_assert_not_reached ();
|
|
return G_TYPE_INVALID;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
|
thunar_renamer_model_get_iter (GtkTreeModel *tree_model,
|
|
GtkTreeIter *iter,
|
|
GtkTreePath *path)
|
|
{
|
|
ThunarRenamerModel *renamer_model = THUNAR_RENAMER_MODEL (tree_model);
|
|
GList *lp;
|
|
|
|
_thunar_return_val_if_fail (THUNAR_IS_RENAMER_MODEL (renamer_model), FALSE);
|
|
_thunar_return_val_if_fail (gtk_tree_path_get_depth (path) > 0, FALSE);
|
|
|
|
lp = g_list_nth (renamer_model->items, gtk_tree_path_get_indices (path)[0]);
|
|
if (G_LIKELY (lp != NULL))
|
|
{
|
|
GTK_TREE_ITER_INIT (*iter, renamer_model->stamp, lp);
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
|
|
static GtkTreePath *
|
|
thunar_renamer_model_get_path (GtkTreeModel *tree_model,
|
|
GtkTreeIter *iter)
|
|
{
|
|
ThunarRenamerModel *renamer_model = THUNAR_RENAMER_MODEL (tree_model);
|
|
gint idx;
|
|
|
|
_thunar_return_val_if_fail (THUNAR_IS_RENAMER_MODEL (renamer_model), NULL);
|
|
_thunar_return_val_if_fail (iter->stamp == renamer_model->stamp, NULL);
|
|
|
|
/* determine the idx of the item */
|
|
idx = g_list_position (renamer_model->items, iter->user_data);
|
|
if (G_UNLIKELY (idx < 0))
|
|
return NULL;
|
|
|
|
return gtk_tree_path_new_from_indices (idx, -1);
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
thunar_renamer_model_get_value (GtkTreeModel *tree_model,
|
|
GtkTreeIter *iter,
|
|
gint column,
|
|
GValue *value)
|
|
{
|
|
ThunarRenamerModelItem *item;
|
|
|
|
_thunar_return_if_fail (iter->stamp == THUNAR_RENAMER_MODEL (tree_model)->stamp);
|
|
_thunar_return_if_fail (THUNAR_IS_RENAMER_MODEL (tree_model));
|
|
|
|
item = g_list_nth_data (iter->user_data, 0);
|
|
|
|
switch (column)
|
|
{
|
|
case THUNAR_RENAMER_MODEL_COLUMN_CONFLICT:
|
|
g_value_init (value, G_TYPE_BOOLEAN);
|
|
g_value_set_boolean (value, item->conflict);
|
|
break;
|
|
|
|
case THUNAR_RENAMER_MODEL_COLUMN_CONFLICT_WEIGHT:
|
|
g_value_init (value, PANGO_TYPE_WEIGHT);
|
|
g_value_set_enum (value, item->conflict ? PANGO_WEIGHT_BOLD : PANGO_WEIGHT_NORMAL);
|
|
break;
|
|
|
|
case THUNAR_RENAMER_MODEL_COLUMN_FILE:
|
|
g_value_init (value, THUNAR_TYPE_FILE);
|
|
g_value_set_object (value, item->file);
|
|
break;
|
|
|
|
case THUNAR_RENAMER_MODEL_COLUMN_NEWNAME:
|
|
g_value_init (value, G_TYPE_STRING);
|
|
if (G_LIKELY (item->name != NULL))
|
|
g_value_set_string (value, item->name);
|
|
else if (item->conflict)
|
|
g_value_set_static_string (value, thunar_file_get_basename (item->file));
|
|
else
|
|
g_value_set_static_string (value, "");
|
|
break;
|
|
|
|
case THUNAR_RENAMER_MODEL_COLUMN_OLDNAME:
|
|
g_value_init (value, G_TYPE_STRING);
|
|
g_value_set_static_string (value, thunar_file_get_basename (item->file));
|
|
break;
|
|
|
|
default:
|
|
_thunar_assert_not_reached ();
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
|
thunar_renamer_model_iter_next (GtkTreeModel *tree_model,
|
|
GtkTreeIter *iter)
|
|
{
|
|
_thunar_return_val_if_fail (THUNAR_IS_RENAMER_MODEL (tree_model), FALSE);
|
|
_thunar_return_val_if_fail (iter->stamp == THUNAR_RENAMER_MODEL (tree_model)->stamp, FALSE);
|
|
|
|
iter->user_data = g_list_next (iter->user_data);
|
|
return (iter->user_data != NULL);
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
|
thunar_renamer_model_iter_children (GtkTreeModel *tree_model,
|
|
GtkTreeIter *iter,
|
|
GtkTreeIter *parent)
|
|
{
|
|
ThunarRenamerModel *renamer_model = THUNAR_RENAMER_MODEL (tree_model);
|
|
|
|
_thunar_return_val_if_fail (THUNAR_IS_RENAMER_MODEL (renamer_model), FALSE);
|
|
|
|
if (G_LIKELY (parent == NULL && renamer_model->items != NULL))
|
|
{
|
|
GTK_TREE_ITER_INIT (*iter, renamer_model->stamp, renamer_model->items);
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
|
thunar_renamer_model_iter_has_child (GtkTreeModel *tree_model,
|
|
GtkTreeIter *iter)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
|
|
static gint
|
|
thunar_renamer_model_iter_n_children (GtkTreeModel *tree_model,
|
|
GtkTreeIter *iter)
|
|
{
|
|
return (iter == NULL) ? g_list_length (THUNAR_RENAMER_MODEL (tree_model)->items) : 0;
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
|
thunar_renamer_model_iter_nth_child (GtkTreeModel *tree_model,
|
|
GtkTreeIter *iter,
|
|
GtkTreeIter *parent,
|
|
gint n)
|
|
{
|
|
ThunarRenamerModel *renamer_model = THUNAR_RENAMER_MODEL (tree_model);
|
|
|
|
_thunar_return_val_if_fail (THUNAR_IS_RENAMER_MODEL (renamer_model), FALSE);
|
|
|
|
if (G_LIKELY (parent == NULL))
|
|
{
|
|
GTK_TREE_ITER_INIT (*iter, renamer_model->stamp, g_list_nth (renamer_model->items, n));
|
|
return (iter->user_data != NULL);
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
|
thunar_renamer_model_iter_parent (GtkTreeModel *tree_model,
|
|
GtkTreeIter *iter,
|
|
GtkTreeIter *child)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
thunar_renamer_model_file_changed (ThunarRenamerModel *renamer_model,
|
|
ThunarFile *file)
|
|
{
|
|
ThunarRenamerModelItem *item;
|
|
GtkTreePath *path;
|
|
GtkTreeIter iter;
|
|
GList *lp;
|
|
guint64 date_changed;
|
|
|
|
_thunar_return_if_fail (THUNAR_IS_FILE (file));
|
|
_thunar_return_if_fail (THUNAR_IS_RENAMER_MODEL (renamer_model));
|
|
|
|
/* check if we have that file */
|
|
for (lp = renamer_model->items; lp != NULL; lp = lp->next)
|
|
if (THUNAR_RENAMER_MODEL_ITEM (lp->data)->file == file)
|
|
{
|
|
item = THUNAR_RENAMER_MODEL_ITEM (lp->data);
|
|
|
|
/* check if the file changed on disk, this is done to prevent
|
|
* excessive looping when some renamers are used
|
|
* (thunar-media-tags-plugin is an example) */
|
|
date_changed = thunar_file_get_date (file, THUNAR_FILE_DATE_CHANGED);
|
|
if (item->date_changed == date_changed)
|
|
break;
|
|
|
|
/* check if we're frozen */
|
|
if (G_LIKELY (!renamer_model->frozen))
|
|
{
|
|
/* the file changed */
|
|
item->changed = TRUE;
|
|
|
|
/* set the new mtime */
|
|
item->date_changed = date_changed;
|
|
|
|
/* invalidate the item */
|
|
thunar_renamer_model_invalidate_item (renamer_model, item);
|
|
break;
|
|
}
|
|
|
|
/* determine the iter for the item */
|
|
GTK_TREE_ITER_INIT (iter, renamer_model->stamp, lp);
|
|
|
|
/* emit "row-changed" to display up2date file name */
|
|
path = gtk_tree_model_get_path (GTK_TREE_MODEL (renamer_model), &iter);
|
|
gtk_tree_model_row_changed (GTK_TREE_MODEL (renamer_model), path, &iter);
|
|
gtk_tree_path_free (path);
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
thunar_renamer_model_file_destroyed (ThunarRenamerModel *renamer_model,
|
|
ThunarFile *file)
|
|
{
|
|
GtkTreePath *path;
|
|
GList *lp;
|
|
gint idx;
|
|
|
|
_thunar_return_if_fail (THUNAR_IS_RENAMER_MODEL (renamer_model));
|
|
_thunar_return_if_fail (THUNAR_IS_FILE (file));
|
|
|
|
/* check if we have that file */
|
|
for (lp = renamer_model->items; lp != NULL; lp = lp->next)
|
|
if (THUNAR_RENAMER_MODEL_ITEM (lp->data)->file == file)
|
|
{
|
|
/* determine the idx of the item */
|
|
idx = g_list_position (renamer_model->items, lp);
|
|
|
|
/* free the item data */
|
|
thunar_renamer_model_release_item (renamer_model, lp->data);
|
|
|
|
/* drop the item from the list */
|
|
renamer_model->items = g_list_delete_link (renamer_model->items, lp);
|
|
|
|
/* tell the view that the item is gone */
|
|
path = gtk_tree_path_new_from_indices (idx, -1);
|
|
gtk_tree_model_row_deleted (GTK_TREE_MODEL (renamer_model), path);
|
|
gtk_tree_path_free (path);
|
|
|
|
/* invalidate all other items */
|
|
thunar_renamer_model_invalidate_all (renamer_model);
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
thunar_renamer_model_invalidate_all (ThunarRenamerModel *renamer_model)
|
|
{
|
|
GList *lp;
|
|
|
|
/* invalidate all items in the model */
|
|
for (lp = renamer_model->items; lp != NULL; lp = lp->next)
|
|
thunar_renamer_model_invalidate_item (renamer_model, lp->data);
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
thunar_renamer_model_invalidate_item (ThunarRenamerModel *renamer_model,
|
|
ThunarRenamerModelItem *item)
|
|
{
|
|
/* mark the item as dirty */
|
|
item->dirty = TRUE;
|
|
|
|
/* check if the update idle source is already running and not frozen */
|
|
if (G_UNLIKELY (renamer_model->update_idle_id == 0 && !renamer_model->frozen))
|
|
{
|
|
/* schedule the update idle source */
|
|
renamer_model->update_idle_id = g_idle_add_full (G_PRIORITY_LOW, thunar_renamer_model_update_idle,
|
|
renamer_model, thunar_renamer_model_update_idle_destroy);
|
|
|
|
/* notify listeners that we're updating */
|
|
g_object_notify (G_OBJECT (renamer_model), "can-rename");
|
|
}
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
|
trm_same_directory (ThunarFile *a,
|
|
ThunarFile *b)
|
|
{
|
|
GFile *parent_a;
|
|
GFile *parent_b;
|
|
gboolean result;
|
|
|
|
/* determine the parent paths for both files */
|
|
parent_a = g_file_get_parent (thunar_file_get_file (a));
|
|
parent_b = g_file_get_parent (thunar_file_get_file (b));
|
|
|
|
/* check if both files have the same parent */
|
|
result = (parent_a != NULL && parent_b != NULL && g_file_equal (parent_a, parent_b));
|
|
|
|
if (parent_a != NULL)
|
|
g_object_unref (parent_a);
|
|
if (parent_b != NULL)
|
|
g_object_unref (parent_b);
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
|
thunar_renamer_model_conflict_item (ThunarRenamerModel *renamer_model,
|
|
ThunarRenamerModelItem *item)
|
|
{
|
|
ThunarRenamerModelItem *oitem;
|
|
GtkTreePath *path;
|
|
GtkTreeIter iter;
|
|
gboolean conflict = FALSE;
|
|
GList *lp;
|
|
|
|
for (lp = renamer_model->items; lp != NULL; lp = lp->next)
|
|
{
|
|
/* check if this item is dirty or the item we're about to check */
|
|
oitem = THUNAR_RENAMER_MODEL_ITEM (lp->data);
|
|
if (G_UNLIKELY (oitem == item || oitem->dirty))
|
|
continue;
|
|
|
|
/* check if this other item conflicts with the item in question (can only conflict if in same directory) */
|
|
if (trm_same_directory (item->file, oitem->file)
|
|
&& ((item->name != NULL && oitem->name == NULL && strcmp (item->name, thunar_file_get_basename (oitem->file)) == 0)
|
|
|| (item->name == NULL && oitem->name != NULL && strcmp (thunar_file_get_basename (item->file), oitem->name) == 0)
|
|
|| (item->name != NULL && oitem->name != NULL && strcmp (item->name, oitem->name) == 0)))
|
|
{
|
|
/* check if the other item is already in conflict state */
|
|
if (G_LIKELY (!oitem->conflict))
|
|
{
|
|
/* set to conflict state */
|
|
oitem->conflict = TRUE;
|
|
|
|
/* determine iter for other item */
|
|
GTK_TREE_ITER_INIT (iter, renamer_model->stamp, lp);
|
|
|
|
/* emit "row-changed" for the other item */
|
|
path = gtk_tree_model_get_path (GTK_TREE_MODEL (renamer_model), &iter);
|
|
gtk_tree_model_row_changed (GTK_TREE_MODEL (renamer_model), path, &iter);
|
|
gtk_tree_path_free (path);
|
|
}
|
|
|
|
/* this item conflicts */
|
|
conflict = TRUE;
|
|
}
|
|
}
|
|
|
|
return conflict;
|
|
}
|
|
|
|
|
|
|
|
static gchar *
|
|
thunar_renamer_model_process_item (ThunarRenamerModel *renamer_model,
|
|
ThunarRenamerModelItem *item,
|
|
guint idx)
|
|
{
|
|
ThunarRenamerMode mode;
|
|
const gchar *file_name;
|
|
const gchar *dot;
|
|
gchar *name = NULL;
|
|
gchar *prefix;
|
|
gchar *extension;
|
|
gchar *text;
|
|
|
|
/* no new name if no renamer is set */
|
|
if (G_UNLIKELY (renamer_model->renamer == NULL))
|
|
return NULL;
|
|
|
|
/* determine the current file name of the file */
|
|
file_name = thunar_file_get_basename (item->file);
|
|
|
|
/* determine the extension in the filename */
|
|
dot = thunar_util_str_get_extension (file_name);
|
|
|
|
/* if we don't have a dot, then no "Extension only" rename can take place */
|
|
if (G_LIKELY (dot != NULL || renamer_model->mode != THUNAR_RENAMER_MODE_EXTENSION))
|
|
{
|
|
/* now, for "Name only", we need a dot, otherwise treat everything as name */
|
|
if (renamer_model->mode == THUNAR_RENAMER_MODE_NAME && dot == NULL)
|
|
mode = THUNAR_RENAMER_MODE_BOTH;
|
|
else
|
|
mode = renamer_model->mode;
|
|
|
|
/* determine the new name according to the mode */
|
|
switch (mode)
|
|
{
|
|
case THUNAR_RENAMER_MODE_NAME:
|
|
/* determine the name part of the display name */
|
|
text = g_strndup (file_name, (dot - file_name));
|
|
|
|
/* determine the new name */
|
|
prefix = thunarx_renamer_process (renamer_model->renamer, THUNARX_FILE_INFO (item->file), text, idx);
|
|
|
|
/* determine the new full name */
|
|
name = g_strconcat (prefix, dot, NULL);
|
|
|
|
/* release the temporary strings */
|
|
g_free (prefix);
|
|
g_free (text);
|
|
break;
|
|
|
|
case THUNAR_RENAMER_MODE_EXTENSION:
|
|
/* determine the new extension */
|
|
extension = thunarx_renamer_process (renamer_model->renamer, THUNARX_FILE_INFO (item->file), dot + 1, idx);
|
|
|
|
prefix = g_strndup (file_name, (dot - file_name) + 1);
|
|
name = g_strconcat (prefix, extension, NULL);
|
|
g_free (prefix);
|
|
|
|
/* release the extension */
|
|
g_free (extension);
|
|
break;
|
|
|
|
case THUNAR_RENAMER_MODE_BOTH:
|
|
/* determine the new full name */
|
|
name = thunarx_renamer_process (renamer_model->renamer, THUNARX_FILE_INFO (item->file), file_name, idx);
|
|
break;
|
|
|
|
default:
|
|
_thunar_assert_not_reached ();
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* check if the new name is equal to the old one */
|
|
if (g_strcmp0 (name, file_name) == 0)
|
|
{
|
|
/* just return NULL then */
|
|
g_free (name);
|
|
name = NULL;
|
|
}
|
|
|
|
return name;
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
|
thunar_renamer_model_update_idle (gpointer user_data)
|
|
{
|
|
ThunarRenamerModelItem *item;
|
|
ThunarRenamerModel *renamer_model = THUNAR_RENAMER_MODEL (user_data);
|
|
GtkTreePath *path;
|
|
GtkTreeIter iter;
|
|
gboolean changed = FALSE;
|
|
gboolean conflict;
|
|
guint idx;
|
|
gchar *name;
|
|
GList *lp;
|
|
|
|
/* don't do anything if the model is frozen */
|
|
if (G_LIKELY (!renamer_model->frozen))
|
|
{
|
|
/* process the first dirty item */
|
|
for (idx = 0, lp = renamer_model->items; !changed && lp != NULL; ++idx, lp = lp->next)
|
|
{
|
|
/* check if this item is dirty */
|
|
item = THUNAR_RENAMER_MODEL_ITEM (lp->data);
|
|
if (G_LIKELY (!item->dirty))
|
|
continue;
|
|
|
|
/* check if the file changed */
|
|
changed = item->changed;
|
|
|
|
/* mark as valid, since we're updating right now */
|
|
item->changed = FALSE;
|
|
item->dirty = FALSE;
|
|
|
|
/* determine the new name for the item */
|
|
name = thunar_renamer_model_process_item (renamer_model, item, idx);
|
|
if (g_strcmp0 (item->name, name) != 0)
|
|
{
|
|
/* apply new name */
|
|
g_free (item->name);
|
|
item->name = name;
|
|
|
|
/* the item changed */
|
|
changed = TRUE;
|
|
}
|
|
else
|
|
{
|
|
/* release temporary name */
|
|
g_free (name);
|
|
}
|
|
|
|
/* check if this item conflicts with any other item */
|
|
conflict = thunar_renamer_model_conflict_item (renamer_model, item);
|
|
if (item->conflict != conflict)
|
|
{
|
|
/* apply the new state */
|
|
item->conflict = conflict;
|
|
|
|
/* the item changed */
|
|
changed = TRUE;
|
|
}
|
|
|
|
/* check if the item changed */
|
|
if (G_LIKELY (changed))
|
|
{
|
|
/* generate the iter for the item */
|
|
GTK_TREE_ITER_INIT (iter, renamer_model->stamp, lp);
|
|
|
|
/* emit "row-changed" for this item */
|
|
path = gtk_tree_path_new_from_indices (idx, -1);
|
|
gtk_tree_model_row_changed (GTK_TREE_MODEL (renamer_model), path, &iter);
|
|
gtk_tree_path_free (path);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* keep the idle source as long as any item changed */
|
|
return changed;
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
thunar_renamer_model_update_idle_destroy (gpointer user_data)
|
|
{
|
|
/* reset the update idle id... */
|
|
THUNAR_RENAMER_MODEL (user_data)->update_idle_id = 0;
|
|
|
|
/* ...and notify listeners */
|
|
g_object_notify (G_OBJECT (user_data), "can-rename");
|
|
}
|
|
|
|
|
|
|
|
static ThunarRenamerModelItem *
|
|
thunar_renamer_model_item_new (ThunarRenamerModel *renamer_model,
|
|
ThunarFile *file)
|
|
{
|
|
ThunarRenamerModelItem *item;
|
|
|
|
item = g_slice_new0 (ThunarRenamerModelItem);
|
|
item->file = THUNAR_FILE (g_object_ref (G_OBJECT (file)));
|
|
item->date_changed = thunar_file_get_date (file, THUNAR_FILE_DATE_CHANGED);
|
|
item->dirty = TRUE;
|
|
|
|
/* enable file watch for the file */
|
|
thunar_file_watch (item->file);
|
|
|
|
/* subscribe to relevant signals */
|
|
g_signal_connect_swapped (G_OBJECT (item->file), "changed", G_CALLBACK (thunar_renamer_model_file_changed), renamer_model);
|
|
g_signal_connect_swapped (G_OBJECT (item->file), "destroy", G_CALLBACK (thunar_renamer_model_file_destroyed), renamer_model);
|
|
|
|
return item;
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
thunar_renamer_model_release_item (ThunarRenamerModel *renamer_model,
|
|
ThunarRenamerModelItem *item)
|
|
{
|
|
thunar_file_unwatch (item->file);
|
|
g_signal_handlers_disconnect_by_data (item->file, renamer_model);
|
|
|
|
g_object_unref (G_OBJECT (item->file));
|
|
g_free (item->name);
|
|
g_slice_free (ThunarRenamerModelItem, item);
|
|
}
|
|
|
|
|
|
|
|
static gint
|
|
thunar_renamer_model_cmp_array (gconstpointer pointer_a,
|
|
gconstpointer pointer_b,
|
|
gpointer user_data)
|
|
{
|
|
const SortTuple *a = pointer_a;
|
|
const SortTuple *b = pointer_b;
|
|
|
|
if (G_UNLIKELY (a->position == b->position))
|
|
/* return the sort order for the 'moved' items in the list */
|
|
return a->offset - b->offset;
|
|
else
|
|
/* return the sort order for all other items in the lis */
|
|
return a->position - b->position;
|
|
}
|
|
|
|
|
|
|
|
static gint
|
|
thunar_renamer_model_cmp_name (gconstpointer pointer_a,
|
|
gconstpointer pointer_b,
|
|
gpointer user_data)
|
|
{
|
|
const SortTuple *tuple_a = pointer_a;
|
|
const SortTuple *tuple_b = pointer_b;
|
|
const ThunarRenamerModelItem *a = tuple_a->item->data;
|
|
const ThunarRenamerModelItem *b = tuple_b->item->data;
|
|
GtkSortType sort_order = GPOINTER_TO_INT (user_data);
|
|
gint result;
|
|
|
|
/* sort files by their old filename */
|
|
result = thunar_file_compare_by_name (a->file, b->file, TRUE);
|
|
|
|
/* insert order for desc sorting */
|
|
return sort_order == GTK_SORT_ASCENDING ? result : -result;
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* thunar_renamer_model_new:
|
|
*
|
|
* Allocates a new #ThunarRenamerModel instance.
|
|
*
|
|
* Return value: the newly allocated #ThunarRenamerModel.
|
|
**/
|
|
ThunarRenamerModel *
|
|
thunar_renamer_model_new (void)
|
|
{
|
|
return g_object_new (THUNAR_TYPE_RENAMER_MODEL, NULL);
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* thunar_renamer_model_get_can_rename:
|
|
* @renamer_model : a #ThunarRenamerModel.
|
|
*
|
|
* Returns %TRUE if the @renamer_model contents
|
|
* can be renamed, i.e. if no conflict exist and
|
|
* atleast one file is present.
|
|
*
|
|
* This method will always return %FALSE if the
|
|
* @renamer_model is frozen. See thunar_renamer_model_set_frozen()
|
|
* and thunar_renamer_model_get_frozen().
|
|
*
|
|
* Return value: %TRUE if bulk rename can be performed.
|
|
**/
|
|
static gboolean
|
|
thunar_renamer_model_get_can_rename (ThunarRenamerModel *renamer_model)
|
|
{
|
|
gboolean can_rename = FALSE;
|
|
GList *lp;
|
|
|
|
_thunar_return_val_if_fail (THUNAR_IS_RENAMER_MODEL (renamer_model), FALSE);
|
|
|
|
if (G_LIKELY (renamer_model->renamer != NULL && !renamer_model->frozen && renamer_model->update_idle_id == 0))
|
|
{
|
|
/* check if atleast one item has a new name and no conflicts exist */
|
|
for (lp = renamer_model->items; lp != NULL; lp = lp->next)
|
|
{
|
|
/* check if we have a conflict here */
|
|
if (G_UNLIKELY (THUNAR_RENAMER_MODEL_ITEM (lp->data)->conflict))
|
|
return FALSE;
|
|
|
|
/* check if the item has a new name */
|
|
if (THUNAR_RENAMER_MODEL_ITEM (lp->data)->name != NULL)
|
|
can_rename = TRUE;
|
|
}
|
|
}
|
|
|
|
return can_rename;
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* thunar_renamer_model_get_frozen:
|
|
* @renamer_model : a #ThunarRenamerModel.
|
|
*
|
|
* Returns %TRUE if the @renamer_model<!---->s state is
|
|
* frozen, that is, no updates will be processed on the
|
|
* @renamer_model.
|
|
*
|
|
* Return value: %TRUE if @renamer_model is frozen.
|
|
**/
|
|
static gboolean
|
|
thunar_renamer_model_get_frozen (ThunarRenamerModel *renamer_model)
|
|
{
|
|
_thunar_return_val_if_fail (THUNAR_IS_RENAMER_MODEL (renamer_model), FALSE);
|
|
return renamer_model->frozen;
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* thunar_renamer_model_set_frozen:
|
|
* @renamer_model : a #ThunarRenamerModel.
|
|
* @frozen : %TRUE to freeze the current @renamer_model<!---->s state.
|
|
*
|
|
* If @frozen is %TRUE the current state of the @renamer_model will be frozen
|
|
* and no updates will be processed until you call this method again with a
|
|
* value of %FALSE for @frozen.
|
|
**/
|
|
static void
|
|
thunar_renamer_model_set_frozen (ThunarRenamerModel *renamer_model,
|
|
gboolean frozen)
|
|
{
|
|
_thunar_return_if_fail (THUNAR_IS_RENAMER_MODEL (renamer_model));
|
|
|
|
/* normalize the value */
|
|
frozen = !!frozen;
|
|
|
|
/* check if we have a new value */
|
|
if (G_LIKELY (renamer_model->frozen != frozen))
|
|
{
|
|
/* apply the new setting */
|
|
renamer_model->frozen = frozen;
|
|
|
|
/* change state appropriately */
|
|
if (G_LIKELY (frozen))
|
|
{
|
|
/* cancel any pending update idle source */
|
|
if (G_UNLIKELY (renamer_model->update_idle_id != 0))
|
|
g_source_remove (renamer_model->update_idle_id);
|
|
}
|
|
else
|
|
{
|
|
/* thaw the model, updating all items */
|
|
thunar_renamer_model_invalidate_all (renamer_model);
|
|
}
|
|
|
|
/* notify listeners */
|
|
g_object_freeze_notify (G_OBJECT (renamer_model));
|
|
g_object_notify (G_OBJECT (renamer_model), "can-rename");
|
|
g_object_notify (G_OBJECT (renamer_model), "frozen");
|
|
g_object_thaw_notify (G_OBJECT (renamer_model));
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* thunar_renamer_model_get_mode:
|
|
* @renamer_model : a #ThunarRenamerModel.
|
|
*
|
|
* Returns the #ThunarRenamerMode currently set
|
|
* for @renamer_model.
|
|
*
|
|
* Return value: the mode for @renamer_model.
|
|
**/
|
|
ThunarRenamerMode
|
|
thunar_renamer_model_get_mode (ThunarRenamerModel *renamer_model)
|
|
{
|
|
_thunar_return_val_if_fail (THUNAR_IS_RENAMER_MODEL (renamer_model), THUNAR_RENAMER_MODE_NAME);
|
|
return renamer_model->mode;
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* thunar_renamer_model_set_mode:
|
|
* @renamer_model : a #ThunarRenamerModel.
|
|
* @mode : a #ThunarRenamerMode.
|
|
*
|
|
* Sets the rename operation mode of @renamer_model
|
|
* to @mode and updates all items.
|
|
**/
|
|
static void
|
|
thunar_renamer_model_set_mode (ThunarRenamerModel *renamer_model,
|
|
ThunarRenamerMode mode)
|
|
{
|
|
_thunar_return_if_fail (THUNAR_IS_RENAMER_MODEL (renamer_model));
|
|
|
|
/* check if we're already in the requested mode */
|
|
if (G_UNLIKELY (renamer_model->mode == mode))
|
|
return;
|
|
|
|
/* apply the new mode */
|
|
renamer_model->mode = mode;
|
|
|
|
/* invalidate all items */
|
|
thunar_renamer_model_invalidate_all (renamer_model);
|
|
|
|
/* notify listeners */
|
|
g_object_notify (G_OBJECT (renamer_model), "mode");
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* thunar_renamer_model_get_renamer:
|
|
* @renamer_model : a #ThunarRenamerModel.
|
|
*
|
|
* Returns the #ThunarxRenamer currently used by
|
|
* the specified @renamer_model.
|
|
*
|
|
* Return value: the current renamer for @renamer_model.
|
|
**/
|
|
ThunarxRenamer *
|
|
thunar_renamer_model_get_renamer (ThunarRenamerModel *renamer_model)
|
|
{
|
|
_thunar_return_val_if_fail (THUNAR_IS_RENAMER_MODEL (renamer_model), NULL);
|
|
return renamer_model->renamer;
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* thunar_renamer_model_set_renamer:
|
|
* @renamer_model : a #ThunarRenamerModel.
|
|
* @renamer : a #ThunarxRenamer or %NULL.
|
|
*
|
|
* Sets the renamer for @renamer_model to the
|
|
* specified @renamer.
|
|
**/
|
|
void
|
|
thunar_renamer_model_set_renamer (ThunarRenamerModel *renamer_model,
|
|
ThunarxRenamer *renamer)
|
|
{
|
|
_thunar_return_if_fail (THUNAR_IS_RENAMER_MODEL (renamer_model));
|
|
_thunar_return_if_fail (renamer == NULL || THUNARX_IS_RENAMER (renamer));
|
|
|
|
/* verify that we do not already use the renamer */
|
|
if (G_UNLIKELY (renamer_model->renamer == renamer))
|
|
return;
|
|
|
|
/* disconnect from the previous renamer */
|
|
if (renamer_model->renamer != NULL)
|
|
{
|
|
g_signal_handlers_disconnect_by_func (G_OBJECT (renamer_model->renamer), thunar_renamer_model_invalidate_all, renamer_model);
|
|
g_object_unref (G_OBJECT (renamer_model->renamer));
|
|
}
|
|
|
|
/* activate the new renamer */
|
|
renamer_model->renamer = renamer;
|
|
|
|
/* connect to the new renamer */
|
|
if (G_LIKELY (renamer != NULL))
|
|
{
|
|
g_signal_connect_swapped (G_OBJECT (renamer), "changed", G_CALLBACK (thunar_renamer_model_invalidate_all), renamer_model);
|
|
g_object_ref (G_OBJECT (renamer));
|
|
}
|
|
|
|
/* invalidate all items */
|
|
thunar_renamer_model_invalidate_all (renamer_model);
|
|
|
|
/* notify listeners */
|
|
g_object_notify (G_OBJECT (renamer_model), "renamer");
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* thunar_renamer_model_insert:
|
|
* @renamer_model : a #ThunarRenamerModel.
|
|
* @file : the #ThunarFile to add to @renamer_model.
|
|
* @position : the position in the model. 0 is prepend, -1 is append.
|
|
*
|
|
* Inserts the @file to the @renamer_model at @position.
|
|
**/
|
|
void
|
|
thunar_renamer_model_insert (ThunarRenamerModel *renamer_model,
|
|
ThunarFile *file,
|
|
gint position)
|
|
{
|
|
ThunarRenamerModelItem *item;
|
|
GtkTreePath *path;
|
|
GtkTreeIter iter;
|
|
GList *lp;
|
|
|
|
_thunar_return_if_fail (THUNAR_IS_RENAMER_MODEL (renamer_model));
|
|
_thunar_return_if_fail (THUNAR_IS_FILE (file));
|
|
|
|
/* check if we already have that file */
|
|
for (lp = renamer_model->items; lp != NULL; lp = lp->next)
|
|
if (THUNAR_RENAMER_MODEL_ITEM (lp->data)->file == file)
|
|
return;
|
|
|
|
/* allocate a new item for the file */
|
|
item = thunar_renamer_model_item_new (renamer_model, file);
|
|
|
|
/* append the item to the model */
|
|
renamer_model->items = g_list_insert (renamer_model->items, item, position);
|
|
|
|
/* determine the iterator for the new item */
|
|
GTK_TREE_ITER_INIT (iter, renamer_model->stamp, g_list_find (renamer_model->items, item));
|
|
|
|
/* emit the "row-inserted" signal */
|
|
path = gtk_tree_model_get_path (GTK_TREE_MODEL (renamer_model), &iter);
|
|
gtk_tree_model_row_inserted (GTK_TREE_MODEL (renamer_model), path, &iter);
|
|
gtk_tree_path_free (path);
|
|
|
|
/* invalidate the newly added item */
|
|
thunar_renamer_model_invalidate_item (renamer_model, item);
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* thunar_renamer_model_reorder:
|
|
* @renamer_model : a #ThunarRenamerModel.
|
|
* @tree_paths : the list of #GtkTreePath that need to be moved to
|
|
* @position.
|
|
* @position : the new position for the list of paths.
|
|
*
|
|
* Reorder the treepaths in the model to their new postion in the list
|
|
* and sends an update to the treeview to reorder the list.
|
|
**/
|
|
void
|
|
thunar_renamer_model_reorder (ThunarRenamerModel *renamer_model,
|
|
GList *tree_paths,
|
|
gint position)
|
|
{
|
|
GList *lp, *lprev;
|
|
gint n_items;
|
|
gint *new_order;
|
|
GtkTreePath *path;
|
|
gint k, n, m;
|
|
SortTuple *sort_array;
|
|
|
|
_thunar_return_if_fail (THUNAR_IS_RENAMER_MODEL (renamer_model));
|
|
|
|
/* leave when there is nothing to sort */
|
|
n_items = g_list_length (renamer_model->items);
|
|
if (G_UNLIKELY (n_items <= 1))
|
|
return;
|
|
|
|
/* correct the sort position to match the list items */
|
|
if (tree_paths != NULL)
|
|
{
|
|
if (position == -1 || position > n_items)
|
|
position = n_items;
|
|
}
|
|
|
|
/* be sure to not overuse the stack */
|
|
if (G_LIKELY (n_items < 500))
|
|
sort_array = g_newa (SortTuple, n_items);
|
|
else
|
|
sort_array = g_new (SortTuple, n_items);
|
|
|
|
/* generate the sort array of tuples */
|
|
for (lp = renamer_model->items, m = 0, n = 0; lp != NULL; lp = lp->next, ++n, ++m)
|
|
{
|
|
/* leave a hole in the sort position for the drop items */
|
|
if (G_UNLIKELY (tree_paths != NULL
|
|
&& m == position))
|
|
m++;
|
|
|
|
sort_array[n].offset = n;
|
|
sort_array[n].item = lp;
|
|
sort_array[n].position = m;
|
|
}
|
|
|
|
/* set the requested sort position for the dragged paths */
|
|
for (lp = tree_paths; lp != NULL; lp = lp->next)
|
|
{
|
|
k = CLAMP (gtk_tree_path_get_indices (lp->data)[0], 0, n_items - 1);
|
|
sort_array[k].position = position;
|
|
}
|
|
|
|
/* sort the array using QuickSort */
|
|
if (tree_paths != NULL)
|
|
g_qsort_with_data (sort_array, n_items, sizeof (SortTuple), thunar_renamer_model_cmp_array, NULL);
|
|
else
|
|
g_qsort_with_data (sort_array, n_items, sizeof (SortTuple), thunar_renamer_model_cmp_name, GINT_TO_POINTER (position));
|
|
|
|
/* update our internals and generate the new order */
|
|
new_order = g_newa (gint, n_items);
|
|
for (n = 0, lprev = NULL; n < n_items; ++n)
|
|
{
|
|
/* set the new order in the sort list */
|
|
new_order[n] = sort_array[n].offset;
|
|
|
|
/* reinsert the item in the list */
|
|
lp = sort_array[n].item;
|
|
lp->next = NULL;
|
|
lp->prev = lprev;
|
|
|
|
/* hookup the item in the list */
|
|
if (G_LIKELY (lprev != NULL))
|
|
lprev->next = lp;
|
|
else
|
|
renamer_model->items = lp;
|
|
|
|
/* advance the offset */
|
|
lprev = lp;
|
|
}
|
|
|
|
/* tell the view about the new item order */
|
|
path = gtk_tree_path_new ();
|
|
gtk_tree_model_rows_reordered (GTK_TREE_MODEL (renamer_model), path, NULL, new_order);
|
|
gtk_tree_path_free (path);
|
|
|
|
/* invalidate all items */
|
|
thunar_renamer_model_invalidate_all (renamer_model);
|
|
|
|
/* cleanup if we used the heap */
|
|
if (G_UNLIKELY (n_items >= 500))
|
|
g_free (sort_array);
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* thunar_renamer_model_sort:
|
|
* @renamer_model : a #ThunarRenamerModel.
|
|
* @sort_order : sort direction of the model.
|
|
*
|
|
* Sort the entire model by the old filename.
|
|
**/
|
|
void
|
|
thunar_renamer_model_sort (ThunarRenamerModel *renamer_model,
|
|
GtkSortType sort_order)
|
|
{
|
|
_thunar_return_if_fail (THUNAR_IS_RENAMER_MODEL (renamer_model));
|
|
|
|
/* abuse function above */
|
|
thunar_renamer_model_reorder (renamer_model, NULL, sort_order);
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* thunar_renamer_model_clear:
|
|
* @renamer_model : a #ThunarRenamerModel.
|
|
*
|
|
* Clears the @renamer_model by removing all files
|
|
* from it.
|
|
**/
|
|
void
|
|
thunar_renamer_model_clear (ThunarRenamerModel *renamer_model)
|
|
{
|
|
_thunar_return_if_fail (THUNAR_IS_RENAMER_MODEL (renamer_model));
|
|
|
|
/* grab an additional reference on the model */
|
|
g_object_ref (G_OBJECT (renamer_model));
|
|
|
|
/* freeze notifications */
|
|
g_object_freeze_notify (G_OBJECT (renamer_model));
|
|
|
|
/* delete all items from the model */
|
|
while (renamer_model->items != NULL)
|
|
{
|
|
/* just use the "file-destroyed" handler here to drop the first item */
|
|
thunar_renamer_model_file_destroyed (renamer_model, THUNAR_RENAMER_MODEL_ITEM (renamer_model->items->data)->file);
|
|
}
|
|
|
|
/* thaw notifications */
|
|
g_object_thaw_notify (G_OBJECT (renamer_model));
|
|
|
|
/* release the additional reference on the model */
|
|
g_object_unref (G_OBJECT (renamer_model));
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* thunar_renamer_model_remove:
|
|
* @renamer_model : a #ThunarRenamerModel.
|
|
* @path : a #GtkTreePath in the @renamer_model.
|
|
*
|
|
* Removes the item identified by the given @path from
|
|
* the @renamer_model.
|
|
**/
|
|
void
|
|
thunar_renamer_model_remove (ThunarRenamerModel *renamer_model,
|
|
GtkTreePath *path)
|
|
{
|
|
GList *lp;
|
|
|
|
_thunar_return_if_fail (THUNAR_IS_RENAMER_MODEL (renamer_model));
|
|
_thunar_return_if_fail (gtk_tree_path_get_depth (path) == 1);
|
|
|
|
/* determine the list item for the path and verify that its valid */
|
|
lp = g_list_nth (renamer_model->items, gtk_tree_path_get_indices (path)[0]);
|
|
if (G_UNLIKELY (lp == NULL))
|
|
return;
|
|
|
|
/* free the item data */
|
|
thunar_renamer_model_release_item (renamer_model, lp->data);
|
|
|
|
/* drop the item from the list */
|
|
renamer_model->items = g_list_delete_link (renamer_model->items, lp);
|
|
|
|
/* tell the view that the item is gone */
|
|
gtk_tree_model_row_deleted (GTK_TREE_MODEL (renamer_model), path);
|
|
|
|
/* invalidate all other items */
|
|
thunar_renamer_model_invalidate_all (renamer_model);
|
|
}
|