755 lines
24 KiB
C
755 lines
24 KiB
C
|
/* PIKA - Photo and Image Kooker Application
|
||
|
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
|
||
|
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
|
||
|
*
|
||
|
* Original copyright, applying to most contents (license remains unchanged):
|
||
|
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
|
||
|
*
|
||
|
* 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 3 of the License, or
|
||
|
* (at your option) any later version.
|
||
|
*
|
||
|
* This program is distributed in the hope that it will be useful,
|
||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
* GNU General Public License for more details.
|
||
|
*
|
||
|
* You should have received a copy of the GNU General Public License
|
||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||
|
*/
|
||
|
|
||
|
#include "config.h"
|
||
|
|
||
|
#include <stdlib.h>
|
||
|
#include <string.h>
|
||
|
|
||
|
#include <gdk-pixbuf/gdk-pixbuf.h>
|
||
|
#include <gegl.h>
|
||
|
|
||
|
#include "libpikabase/pikabase.h"
|
||
|
#include "libpikamath/pikamath.h"
|
||
|
|
||
|
#include "core-types.h"
|
||
|
|
||
|
#include "pika.h"
|
||
|
#include "pikachannel.h"
|
||
|
#include "pikaimage.h"
|
||
|
#include "pikaitem.h"
|
||
|
#include "pikaitemlist.h"
|
||
|
#include "pikalayer.h"
|
||
|
#include "pikamarshal.h"
|
||
|
|
||
|
#include "vectors/pikavectors.h"
|
||
|
|
||
|
#include "pika-intl.h"
|
||
|
|
||
|
|
||
|
enum
|
||
|
{
|
||
|
EMPTY,
|
||
|
LAST_SIGNAL
|
||
|
};
|
||
|
|
||
|
enum
|
||
|
{
|
||
|
PROP_0,
|
||
|
PROP_IMAGE,
|
||
|
PROP_IS_PATTERN,
|
||
|
PROP_SELECT_METHOD,
|
||
|
PROP_ITEMS,
|
||
|
PROP_ITEM_TYPE,
|
||
|
N_PROPS
|
||
|
};
|
||
|
|
||
|
|
||
|
typedef struct _PikaItemListPrivate PikaItemListPrivate;
|
||
|
|
||
|
struct _PikaItemListPrivate
|
||
|
{
|
||
|
PikaImage *image;
|
||
|
|
||
|
gboolean is_pattern; /* Whether a named fixed set or a pattern-search. */
|
||
|
PikaSelectMethod select_method; /* Pattern format if is_pattern is TRUE */
|
||
|
|
||
|
GList *items; /* Fixed item list if is_pattern is TRUE. */
|
||
|
GList *deleted_items; /* Removed item list kept for undoes. */
|
||
|
GType item_type;
|
||
|
};
|
||
|
|
||
|
|
||
|
/* local function prototypes */
|
||
|
|
||
|
static void pika_item_list_constructed (GObject *object);
|
||
|
static void pika_item_list_dispose (GObject *object);
|
||
|
static void pika_item_list_finalize (GObject *object);
|
||
|
static void pika_item_list_set_property (GObject *object,
|
||
|
guint property_id,
|
||
|
const GValue *value,
|
||
|
GParamSpec *pspec);
|
||
|
static void pika_item_list_get_property (GObject *object,
|
||
|
guint property_id,
|
||
|
GValue *value,
|
||
|
GParamSpec *pspec);
|
||
|
|
||
|
static void pika_item_list_item_add (PikaContainer *container,
|
||
|
PikaObject *object,
|
||
|
PikaItemList *set);
|
||
|
static void pika_item_list_item_remove (PikaContainer *container,
|
||
|
PikaObject *object,
|
||
|
PikaItemList *set);
|
||
|
|
||
|
static GList * pika_item_list_get_items_by_substr (PikaItemList *set,
|
||
|
const gchar *pattern,
|
||
|
GError **error);
|
||
|
static GList * pika_item_list_get_items_by_glob (PikaItemList *set,
|
||
|
const gchar *pattern,
|
||
|
GError **error);
|
||
|
static GList * pika_item_list_get_items_by_regexp (PikaItemList *set,
|
||
|
const gchar *pattern,
|
||
|
GError **error);
|
||
|
static void pika_item_list_clean_deleted_items (PikaItemList *set,
|
||
|
PikaItem *searched,
|
||
|
gboolean *found);
|
||
|
static void pika_item_list_free_deleted_item (GWeakRef *item);
|
||
|
|
||
|
|
||
|
G_DEFINE_TYPE_WITH_PRIVATE (PikaItemList, pika_item_list, PIKA_TYPE_OBJECT)
|
||
|
|
||
|
#define parent_class pika_item_list_parent_class
|
||
|
|
||
|
static guint pika_item_list_signals[LAST_SIGNAL] = { 0 };
|
||
|
static GParamSpec *pika_item_list_props[N_PROPS] = { NULL, };
|
||
|
|
||
|
static void
|
||
|
pika_item_list_class_init (PikaItemListClass *klass)
|
||
|
{
|
||
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||
|
|
||
|
/**
|
||
|
* PikaItemList::empty:
|
||
|
*
|
||
|
* Sent when the item set changed and would return an empty set of
|
||
|
* items.
|
||
|
*/
|
||
|
pika_item_list_signals[EMPTY] =
|
||
|
g_signal_new ("empty",
|
||
|
G_TYPE_FROM_CLASS (klass),
|
||
|
G_SIGNAL_RUN_FIRST,
|
||
|
G_STRUCT_OFFSET (PikaItemListClass, empty),
|
||
|
NULL, NULL, NULL,
|
||
|
G_TYPE_NONE, 0);
|
||
|
|
||
|
object_class->constructed = pika_item_list_constructed;
|
||
|
object_class->dispose = pika_item_list_dispose;
|
||
|
object_class->finalize = pika_item_list_finalize;
|
||
|
object_class->set_property = pika_item_list_set_property;
|
||
|
object_class->get_property = pika_item_list_get_property;
|
||
|
|
||
|
pika_item_list_props[PROP_IMAGE] = g_param_spec_object ("image", NULL, NULL,
|
||
|
PIKA_TYPE_IMAGE,
|
||
|
PIKA_PARAM_READWRITE |
|
||
|
G_PARAM_CONSTRUCT_ONLY);
|
||
|
pika_item_list_props[PROP_IS_PATTERN] = g_param_spec_boolean ("is-pattern", NULL, NULL,
|
||
|
FALSE,
|
||
|
PIKA_PARAM_READWRITE |
|
||
|
G_PARAM_CONSTRUCT_ONLY);
|
||
|
pika_item_list_props[PROP_SELECT_METHOD] = g_param_spec_enum ("select-method", NULL, NULL,
|
||
|
PIKA_TYPE_SELECT_METHOD,
|
||
|
PIKA_SELECT_PLAIN_TEXT,
|
||
|
PIKA_PARAM_READWRITE |
|
||
|
G_PARAM_CONSTRUCT_ONLY);
|
||
|
pika_item_list_props[PROP_ITEMS] = g_param_spec_pointer ("items",
|
||
|
NULL, NULL,
|
||
|
PIKA_PARAM_READWRITE |
|
||
|
G_PARAM_CONSTRUCT_ONLY);
|
||
|
pika_item_list_props[PROP_ITEM_TYPE] = g_param_spec_gtype ("item-type",
|
||
|
NULL, NULL,
|
||
|
G_TYPE_NONE,
|
||
|
PIKA_PARAM_READWRITE |
|
||
|
G_PARAM_CONSTRUCT_ONLY);
|
||
|
|
||
|
g_object_class_install_properties (object_class, N_PROPS, pika_item_list_props);
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
pika_item_list_init (PikaItemList *set)
|
||
|
{
|
||
|
set->p = pika_item_list_get_instance_private (set);
|
||
|
|
||
|
set->p->items = NULL;
|
||
|
set->p->select_method = PIKA_SELECT_PLAIN_TEXT;
|
||
|
set->p->is_pattern = FALSE;
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
pika_item_list_constructed (GObject *object)
|
||
|
{
|
||
|
PikaItemList *set = PIKA_ITEM_LIST (object);
|
||
|
|
||
|
G_OBJECT_CLASS (parent_class)->constructed (object);
|
||
|
|
||
|
pika_assert (PIKA_IS_IMAGE (set->p->image));
|
||
|
pika_assert (set->p->item_type == PIKA_TYPE_LAYER ||
|
||
|
set->p->item_type == PIKA_TYPE_VECTORS ||
|
||
|
set->p->item_type == PIKA_TYPE_CHANNEL);
|
||
|
|
||
|
if (! set->p->is_pattern)
|
||
|
{
|
||
|
PikaContainer *container;
|
||
|
|
||
|
if (set->p->item_type == PIKA_TYPE_LAYER)
|
||
|
container = pika_image_get_layers (set->p->image);
|
||
|
else if (set->p->item_type == PIKA_TYPE_VECTORS)
|
||
|
container = pika_image_get_vectors (set->p->image);
|
||
|
else
|
||
|
container = pika_image_get_channels (set->p->image);
|
||
|
g_signal_connect (container, "remove",
|
||
|
G_CALLBACK (pika_item_list_item_remove),
|
||
|
set);
|
||
|
g_signal_connect (container, "add",
|
||
|
G_CALLBACK (pika_item_list_item_add),
|
||
|
set);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
pika_item_list_dispose (GObject *object)
|
||
|
{
|
||
|
PikaItemList *set = PIKA_ITEM_LIST (object);
|
||
|
|
||
|
if (! set->p->is_pattern)
|
||
|
{
|
||
|
PikaContainer *container;
|
||
|
|
||
|
if (set->p->item_type == PIKA_TYPE_LAYER)
|
||
|
container = pika_image_get_layers (set->p->image);
|
||
|
else if (set->p->item_type == PIKA_TYPE_VECTORS)
|
||
|
container = pika_image_get_vectors (set->p->image);
|
||
|
else
|
||
|
container = pika_image_get_channels (set->p->image);
|
||
|
g_signal_handlers_disconnect_by_func (container,
|
||
|
G_CALLBACK (pika_item_list_item_remove),
|
||
|
set);
|
||
|
g_signal_handlers_disconnect_by_func (container,
|
||
|
G_CALLBACK (pika_item_list_item_add),
|
||
|
set);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
pika_item_list_finalize (GObject *object)
|
||
|
{
|
||
|
PikaItemList *set = PIKA_ITEM_LIST (object);
|
||
|
|
||
|
g_list_free (set->p->items);
|
||
|
g_list_free_full (set->p->deleted_items,
|
||
|
(GDestroyNotify) pika_item_list_free_deleted_item);
|
||
|
|
||
|
G_OBJECT_CLASS (parent_class)->finalize (object);
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
pika_item_list_set_property (GObject *object,
|
||
|
guint property_id,
|
||
|
const GValue *value,
|
||
|
GParamSpec *pspec)
|
||
|
{
|
||
|
PikaItemList *set = PIKA_ITEM_LIST (object);
|
||
|
|
||
|
switch (property_id)
|
||
|
{
|
||
|
case PROP_IMAGE:
|
||
|
set->p->image = g_value_get_object (value);
|
||
|
break;
|
||
|
case PROP_IS_PATTERN:
|
||
|
set->p->is_pattern = g_value_get_boolean (value);
|
||
|
break;
|
||
|
case PROP_SELECT_METHOD:
|
||
|
set->p->select_method = g_value_get_enum (value);
|
||
|
break;
|
||
|
case PROP_ITEMS:
|
||
|
set->p->items = g_list_copy (g_value_get_pointer (value));
|
||
|
break;
|
||
|
case PROP_ITEM_TYPE:
|
||
|
set->p->item_type = g_value_get_gtype (value);
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
pika_item_list_get_property (GObject *object,
|
||
|
guint property_id,
|
||
|
GValue *value,
|
||
|
GParamSpec *pspec)
|
||
|
{
|
||
|
PikaItemList *set = PIKA_ITEM_LIST (object);
|
||
|
|
||
|
switch (property_id)
|
||
|
{
|
||
|
case PROP_IMAGE:
|
||
|
g_value_set_object (value, set->p->image);
|
||
|
break;
|
||
|
case PROP_IS_PATTERN:
|
||
|
g_value_set_boolean (value, set->p->is_pattern);
|
||
|
break;
|
||
|
case PROP_SELECT_METHOD:
|
||
|
g_value_set_enum (value, set->p->select_method);
|
||
|
break;
|
||
|
case PROP_ITEMS:
|
||
|
g_value_set_pointer (value, set->p->items);
|
||
|
break;
|
||
|
case PROP_ITEM_TYPE:
|
||
|
g_value_set_gtype (value, set->p->item_type);
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/* Public functions */
|
||
|
|
||
|
|
||
|
/**
|
||
|
* pika_item_list_named_new:
|
||
|
* @image: The new item_list's #PikaImage.
|
||
|
* @item_type: The type of #PikaItem in the list.
|
||
|
* @name: The name to assign the item list.
|
||
|
* @items: The items in the list.
|
||
|
*
|
||
|
* Create a fixed list of items made of items. It cannot be edited and
|
||
|
* will only auto-update when items get deleted from @image, until the
|
||
|
* list reaches 0 (in which case, the list will self-destroy).
|
||
|
*
|
||
|
* If @items is %NULL, the current item selection of type @item_type in
|
||
|
* @image is used. If this selection is empty, then %NULL is returned.
|
||
|
*
|
||
|
* Returns: The newly created #PikaItemList of %NULL if it corresponds
|
||
|
* to no items.
|
||
|
*/
|
||
|
PikaItemList *
|
||
|
pika_item_list_named_new (PikaImage *image,
|
||
|
GType item_type,
|
||
|
const gchar *name,
|
||
|
GList *items)
|
||
|
{
|
||
|
PikaItemList *set;
|
||
|
GList *iter;
|
||
|
|
||
|
g_return_val_if_fail (g_type_is_a (item_type, PIKA_TYPE_ITEM), NULL);
|
||
|
g_return_val_if_fail (PIKA_IS_IMAGE (image), NULL);
|
||
|
|
||
|
for (iter = items; iter; iter = iter->next)
|
||
|
g_return_val_if_fail (g_type_is_a (G_OBJECT_TYPE (iter->data), item_type), NULL);
|
||
|
|
||
|
if (! items)
|
||
|
{
|
||
|
if (item_type == PIKA_TYPE_LAYER)
|
||
|
items = pika_image_get_selected_layers (image);
|
||
|
else if (item_type == PIKA_TYPE_VECTORS)
|
||
|
items = pika_image_get_selected_vectors (image);
|
||
|
else if (item_type == PIKA_TYPE_CHANNEL)
|
||
|
items = pika_image_get_selected_channels (image);
|
||
|
|
||
|
if (! items)
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
set = g_object_new (PIKA_TYPE_ITEM_LIST,
|
||
|
"image", image,
|
||
|
"name", name,
|
||
|
"is-pattern", FALSE,
|
||
|
"item-type", item_type,
|
||
|
"items", items,
|
||
|
NULL);
|
||
|
|
||
|
return set;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* pika_item_list_pattern_new:
|
||
|
* @image: The new item_list's #PikaImage.
|
||
|
* @item_type: The type of #PikaItem in the list.
|
||
|
* @pattern_syntax: type of patterns we are handling.
|
||
|
* @pattern: The pattern generating the contents of the list.
|
||
|
*
|
||
|
* Create a list of items generated from a pattern. It cannot be edited.
|
||
|
*
|
||
|
* Returns: The newly created #PikaItemList.
|
||
|
*/
|
||
|
PikaItemList *
|
||
|
pika_item_list_pattern_new (PikaImage *image,
|
||
|
GType item_type,
|
||
|
PikaSelectMethod pattern_syntax,
|
||
|
const gchar *pattern)
|
||
|
{
|
||
|
PikaItemList *set;
|
||
|
|
||
|
g_return_val_if_fail (g_type_is_a (item_type, PIKA_TYPE_ITEM), NULL);
|
||
|
g_return_val_if_fail (PIKA_IS_IMAGE (image), NULL);
|
||
|
|
||
|
/* TODO: check pattern first and fail if invalid. */
|
||
|
set = g_object_new (PIKA_TYPE_ITEM_LIST,
|
||
|
"image", image,
|
||
|
"name", pattern,
|
||
|
"is-pattern", TRUE,
|
||
|
"select-method", pattern_syntax,
|
||
|
"item-type", item_type,
|
||
|
NULL);
|
||
|
|
||
|
return set;
|
||
|
}
|
||
|
|
||
|
GType
|
||
|
pika_item_list_get_item_type (PikaItemList *set)
|
||
|
{
|
||
|
g_return_val_if_fail (PIKA_IS_ITEM_LIST (set), FALSE);
|
||
|
|
||
|
return set->p->item_type;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* pika_item_list_get_items:
|
||
|
* @set:
|
||
|
*
|
||
|
* Returns: (transfer container): The unordered list of items
|
||
|
* represented by @set to be freed with g_list_free().
|
||
|
*/
|
||
|
GList *
|
||
|
pika_item_list_get_items (PikaItemList *set,
|
||
|
GError **error)
|
||
|
{
|
||
|
GList *items = NULL;
|
||
|
|
||
|
g_return_val_if_fail (PIKA_IS_ITEM_LIST (set), NULL);
|
||
|
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
|
||
|
|
||
|
if (set->p->is_pattern)
|
||
|
{
|
||
|
switch (set->p->select_method)
|
||
|
{
|
||
|
case PIKA_SELECT_PLAIN_TEXT:
|
||
|
items = pika_item_list_get_items_by_substr (set,
|
||
|
pika_object_get_name (set),
|
||
|
error);
|
||
|
break;
|
||
|
case PIKA_SELECT_GLOB_PATTERN:
|
||
|
items = pika_item_list_get_items_by_glob (set,
|
||
|
pika_object_get_name (set),
|
||
|
error);
|
||
|
break;
|
||
|
case PIKA_SELECT_REGEX_PATTERN:
|
||
|
items = pika_item_list_get_items_by_regexp (set,
|
||
|
pika_object_get_name (set),
|
||
|
error);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
items = g_list_copy (set->p->items);
|
||
|
}
|
||
|
|
||
|
return items;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* pika_item_list_is_pattern:
|
||
|
* @set: The #PikaItemList.
|
||
|
* @pattern_syntax: The type of patterns @set handles.
|
||
|
*
|
||
|
* Indicate if @set is a pattern list. If the returned value is %TRUE,
|
||
|
* then @pattern_syntax will be set to the syntax we are dealing with.
|
||
|
*
|
||
|
* Returns: %TRUE if @set is a pattern list, %FALSE if it is a named
|
||
|
* list.
|
||
|
*/
|
||
|
gboolean
|
||
|
pika_item_list_is_pattern (PikaItemList *set,
|
||
|
PikaSelectMethod *pattern_syntax)
|
||
|
{
|
||
|
g_return_val_if_fail (PIKA_IS_ITEM_LIST (set), FALSE);
|
||
|
|
||
|
if (set->p->is_pattern && pattern_syntax)
|
||
|
*pattern_syntax = set->p->select_method;
|
||
|
|
||
|
return (set->p->is_pattern);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* pika_item_list_is_pattern:
|
||
|
* @set: The #PikaItemList.
|
||
|
* @item: #PikaItem to add to @set.
|
||
|
*
|
||
|
* Add @item to the named list @set whose item type must also agree.
|
||
|
*/
|
||
|
void
|
||
|
pika_item_list_add (PikaItemList *set,
|
||
|
PikaItem *item)
|
||
|
{
|
||
|
g_return_if_fail (PIKA_IS_ITEM_LIST (set));
|
||
|
g_return_if_fail (! pika_item_list_is_pattern (set, NULL));
|
||
|
g_return_if_fail (g_type_is_a (G_TYPE_FROM_INSTANCE (item), set->p->item_type));
|
||
|
|
||
|
set->p->items = g_list_prepend (set->p->items, item);
|
||
|
}
|
||
|
|
||
|
|
||
|
/* Private functions */
|
||
|
|
||
|
|
||
|
static void
|
||
|
pika_item_list_item_add (PikaContainer *container,
|
||
|
PikaObject *object,
|
||
|
PikaItemList *set)
|
||
|
{
|
||
|
gboolean found = FALSE;
|
||
|
|
||
|
pika_item_list_clean_deleted_items (set, PIKA_ITEM (object), &found);
|
||
|
|
||
|
if (found)
|
||
|
{
|
||
|
/* Such an item can only have been added back as part of an redo
|
||
|
* step.
|
||
|
*/
|
||
|
set->p->items = g_list_prepend (set->p->items, object);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
pika_item_list_item_remove (PikaContainer *container,
|
||
|
PikaObject *object,
|
||
|
PikaItemList *set)
|
||
|
{
|
||
|
GWeakRef *deleted_item = g_slice_new (GWeakRef);
|
||
|
|
||
|
/* Keep a weak link on object so that it disappears by itself when no
|
||
|
* other piece of code has a reference to it. In particular, we expect
|
||
|
* undo to keep references to deleted items. So if a redo happens we
|
||
|
* will get a "add" signal with the same object.
|
||
|
*/
|
||
|
set->p->items = g_list_remove (set->p->items, object);
|
||
|
|
||
|
g_weak_ref_init (deleted_item, object);
|
||
|
set->p->deleted_items = g_list_prepend (set->p->deleted_items, deleted_item);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* @pika_item_list_get_items_by_substr:
|
||
|
* @image:
|
||
|
* @pattern:
|
||
|
* @error: unused #GError.
|
||
|
*
|
||
|
* Replace currently selected items in @image with the items whose
|
||
|
* names match with the @pattern after tokenisation, case-folding and
|
||
|
* normalization.
|
||
|
*
|
||
|
* Returns: %TRUE if some items matched @pattern (even if it turned out
|
||
|
* selected items stay the same), %FALSE otherwise.
|
||
|
*/
|
||
|
static GList *
|
||
|
pika_item_list_get_items_by_substr (PikaItemList *set,
|
||
|
const gchar *pattern,
|
||
|
GError **error)
|
||
|
{
|
||
|
GList *items;
|
||
|
GList *match = NULL;
|
||
|
GList *iter;
|
||
|
|
||
|
g_return_val_if_fail (PIKA_IS_ITEM_LIST (set), FALSE);
|
||
|
g_return_val_if_fail (error && *error == NULL, FALSE);
|
||
|
|
||
|
if (pattern == NULL)
|
||
|
return NULL;
|
||
|
|
||
|
if (set->p->item_type == PIKA_TYPE_LAYER)
|
||
|
{
|
||
|
items = pika_image_get_layer_list (set->p->image);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
g_critical ("%s: only list of PikaLayer supported for now.",
|
||
|
G_STRFUNC);
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
for (iter = items; iter; iter = iter->next)
|
||
|
{
|
||
|
if (g_str_match_string (pattern,
|
||
|
pika_object_get_name (iter->data),
|
||
|
TRUE))
|
||
|
match = g_list_prepend (match, iter->data);
|
||
|
}
|
||
|
|
||
|
return match;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* @pika_item_list_get_items_by_glob:
|
||
|
* @image:
|
||
|
* @pattern:
|
||
|
* @error: unused #GError.
|
||
|
*
|
||
|
* Replace currently selected items in @image with the items whose
|
||
|
* names match with the @pattern glob expression.
|
||
|
*
|
||
|
* Returns: %TRUE if some items matched @pattern (even if it turned out
|
||
|
* selected items stay the same), %FALSE otherwise.
|
||
|
*/
|
||
|
static GList *
|
||
|
pika_item_list_get_items_by_glob (PikaItemList *set,
|
||
|
const gchar *pattern,
|
||
|
GError **error)
|
||
|
{
|
||
|
GList *items;
|
||
|
GList *match = NULL;
|
||
|
GList *iter;
|
||
|
GPatternSpec *spec;
|
||
|
|
||
|
g_return_val_if_fail (PIKA_IS_ITEM_LIST (set), FALSE);
|
||
|
g_return_val_if_fail (error && *error == NULL, FALSE);
|
||
|
|
||
|
if (pattern == NULL)
|
||
|
return NULL;
|
||
|
|
||
|
if (set->p->item_type == PIKA_TYPE_LAYER)
|
||
|
{
|
||
|
items = pika_image_get_layer_list (set->p->image);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
g_critical ("%s: only list of PikaLayer supported for now.",
|
||
|
G_STRFUNC);
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
spec = g_pattern_spec_new (pattern);
|
||
|
for (iter = items; iter; iter = iter->next)
|
||
|
{
|
||
|
if (g_pattern_spec_match_string (spec, pika_object_get_name (iter->data)))
|
||
|
match = g_list_prepend (match, iter->data);
|
||
|
}
|
||
|
g_pattern_spec_free (spec);
|
||
|
|
||
|
return match;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* @pika_item_list_get_items_by_regexp:
|
||
|
* @image:
|
||
|
* @pattern:
|
||
|
* @error:
|
||
|
*
|
||
|
* Replace currently selected items in @image with the items whose
|
||
|
* names match with the @pattern regular expression.
|
||
|
*
|
||
|
* Returns: %TRUE if some items matched @pattern (even if it turned out
|
||
|
* selected items stay the same), %FALSE otherwise or if
|
||
|
* @pattern is an invalid regular expression (in which case,
|
||
|
* @error will be filled with the appropriate error).
|
||
|
*/
|
||
|
static GList *
|
||
|
pika_item_list_get_items_by_regexp (PikaItemList *set,
|
||
|
const gchar *pattern,
|
||
|
GError **error)
|
||
|
{
|
||
|
GList *items;
|
||
|
GList *match = NULL;
|
||
|
GList *iter;
|
||
|
GRegex *regex;
|
||
|
|
||
|
g_return_val_if_fail (PIKA_IS_ITEM_LIST (set), FALSE);
|
||
|
g_return_val_if_fail (pattern != NULL, FALSE);
|
||
|
g_return_val_if_fail (error && *error == NULL, FALSE);
|
||
|
|
||
|
regex = g_regex_new (pattern, 0, 0, error);
|
||
|
|
||
|
if (regex == NULL)
|
||
|
return NULL;
|
||
|
|
||
|
if (set->p->item_type == PIKA_TYPE_LAYER)
|
||
|
{
|
||
|
items = pika_image_get_layer_list (set->p->image);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
g_critical ("%s: only list of PikaLayer supported for now.",
|
||
|
G_STRFUNC);
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
for (iter = items; iter; iter = iter->next)
|
||
|
{
|
||
|
if (g_regex_match (regex,
|
||
|
pika_object_get_name (iter->data),
|
||
|
0, NULL))
|
||
|
match = g_list_prepend (match, iter->data);
|
||
|
}
|
||
|
g_regex_unref (regex);
|
||
|
|
||
|
return match;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Remove all deleted items which don't have any reference left anywhere
|
||
|
* (only leaving the shell of the weak reference), hence whose deletion
|
||
|
* cannot be undone anyway.
|
||
|
* If @searched is not %NULL, check if it belonged to the deleted item
|
||
|
* list and return TRUE if so. In this case, you must call the function
|
||
|
* with @found set to %FALSE initially.
|
||
|
*/
|
||
|
static void
|
||
|
pika_item_list_clean_deleted_items (PikaItemList *set,
|
||
|
PikaItem *searched,
|
||
|
gboolean *found)
|
||
|
{
|
||
|
GList *iter;
|
||
|
|
||
|
g_return_if_fail (PIKA_IS_ITEM_LIST (set));
|
||
|
g_return_if_fail (! searched || (found && *found == FALSE));
|
||
|
|
||
|
for (iter = set->p->deleted_items; iter; iter = iter->next)
|
||
|
{
|
||
|
PikaItem *item = g_weak_ref_get (iter->data);
|
||
|
|
||
|
if (item == NULL)
|
||
|
{
|
||
|
set->p->deleted_items = g_list_delete_link (set->p->deleted_items,
|
||
|
iter);
|
||
|
break;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (searched && item == searched)
|
||
|
{
|
||
|
set->p->deleted_items = g_list_delete_link (set->p->deleted_items,
|
||
|
iter);
|
||
|
*found = TRUE;
|
||
|
g_object_unref (item);
|
||
|
break;
|
||
|
}
|
||
|
g_object_unref (item);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (iter)
|
||
|
pika_item_list_clean_deleted_items (set,
|
||
|
(found && *found) ? NULL : searched,
|
||
|
found);
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
pika_item_list_free_deleted_item (GWeakRef *item)
|
||
|
{
|
||
|
g_weak_ref_clear (item);
|
||
|
|
||
|
g_slice_free (GWeakRef, item);
|
||
|
}
|