gimptaggedcontainer.c 16.6 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
/* GIMP - The GNU Image Manipulation Program
 * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis
 *
 * gimptaggedcontainer.c
 * Copyright (C) 2008 Aurimas Juška <aurisj@svn.gnome.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 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
18
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
19 20 21 22
 */

#include "config.h"

23
#include <gio/gio.h>
24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40

#include "core-types.h"

#include "gimp.h"
#include "gimpmarshal.h"
#include "gimptag.h"
#include "gimptagged.h"
#include "gimptaggedcontainer.h"


enum
{
  TAG_COUNT_CHANGED,
  LAST_SIGNAL
};


41 42 43 44
static void      gimp_tagged_container_dispose            (GObject               *object);
static gint64    gimp_tagged_container_get_memsize        (GimpObject            *object,
                                                           gint64                *gui_size);

45 46
static void      gimp_tagged_container_clear              (GimpContainer         *container);

47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72
static void      gimp_tagged_container_src_add            (GimpFilteredContainer *filtered_container,
                                                           GimpObject            *object);
static void      gimp_tagged_container_src_remove         (GimpFilteredContainer *filtered_container,
                                                           GimpObject            *object);
static void      gimp_tagged_container_src_freeze         (GimpFilteredContainer *filtered_container);
static void      gimp_tagged_container_src_thaw           (GimpFilteredContainer *filtered_container);

static gboolean  gimp_tagged_container_object_matches     (GimpTaggedContainer   *tagged_container,
                                                           GimpObject            *object);

static void      gimp_tagged_container_tag_added          (GimpTagged            *tagged,
                                                           GimpTag               *tag,
                                                           GimpTaggedContainer   *tagged_container);
static void      gimp_tagged_container_tag_removed        (GimpTagged            *tagged,
                                                           GimpTag               *tag,
                                                           GimpTaggedContainer   *tagged_container);
static void      gimp_tagged_container_ref_tag            (GimpTaggedContainer   *tagged_container,
                                                           GimpTag               *tag);
static void      gimp_tagged_container_unref_tag          (GimpTaggedContainer   *tagged_container,
                                                           GimpTag               *tag);
static void      gimp_tagged_container_tag_count_changed  (GimpTaggedContainer   *tagged_container,
                                                           gint                   tag_count);


G_DEFINE_TYPE (GimpTaggedContainer, gimp_tagged_container,
               GIMP_TYPE_FILTERED_CONTAINER)
73 74 75 76 77 78 79 80 81

#define parent_class gimp_tagged_container_parent_class

static guint gimp_tagged_container_signals[LAST_SIGNAL] = { 0, };


static void
gimp_tagged_container_class_init (GimpTaggedContainerClass *klass)
{
82 83
  GObjectClass               *g_object_class    = G_OBJECT_CLASS (klass);
  GimpObjectClass            *gimp_object_class = GIMP_OBJECT_CLASS (klass);
84
  GimpContainerClass         *container_class   = GIMP_CONTAINER_CLASS (klass);
85
  GimpFilteredContainerClass *filtered_class    = GIMP_FILTERED_CONTAINER_CLASS (klass);
86 87 88 89 90

  g_object_class->dispose        = gimp_tagged_container_dispose;

  gimp_object_class->get_memsize = gimp_tagged_container_get_memsize;

91 92
  container_class->clear         = gimp_tagged_container_clear;

93 94 95 96 97
  filtered_class->src_add        = gimp_tagged_container_src_add;
  filtered_class->src_remove     = gimp_tagged_container_src_remove;
  filtered_class->src_freeze     = gimp_tagged_container_src_freeze;
  filtered_class->src_thaw       = gimp_tagged_container_src_thaw;

98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114
  klass->tag_count_changed       = gimp_tagged_container_tag_count_changed;

  gimp_tagged_container_signals[TAG_COUNT_CHANGED] =
    g_signal_new ("tag-count-changed",
                  GIMP_TYPE_TAGGED_CONTAINER,
                  G_SIGNAL_RUN_LAST,
                  G_STRUCT_OFFSET (GimpTaggedContainerClass, tag_count_changed),
                  NULL, NULL,
                  gimp_marshal_VOID__INT,
                  G_TYPE_NONE, 1,
                  G_TYPE_INT);
}

static void
gimp_tagged_container_init (GimpTaggedContainer *tagged_container)
{
  tagged_container->tag_ref_counts =
115 116 117 118
    g_hash_table_new_full ((GHashFunc) gimp_tag_get_hash,
                           (GEqualFunc) gimp_tag_equals,
                           (GDestroyNotify) g_object_unref,
                           (GDestroyNotify) NULL);
119 120 121 122 123 124 125
}

static void
gimp_tagged_container_dispose (GObject *object)
{
  GimpTaggedContainer *tagged_container = GIMP_TAGGED_CONTAINER (object);

126 127 128 129 130 131 132
  if (tagged_container->filter)
    {
      g_list_free_full (tagged_container->filter,
                        (GDestroyNotify) gimp_tag_or_null_unref);
      tagged_container->filter = NULL;
    }

133
  g_clear_pointer (&tagged_container->tag_ref_counts, g_hash_table_unref);
134 135 136 137

  G_OBJECT_CLASS (parent_class)->dispose (object);
}

138 139 140 141 142 143 144 145 146 147 148 149
static gint64
gimp_tagged_container_get_memsize (GimpObject *object,
                                   gint64     *gui_size)
{
  gint64 memsize = 0;

  /* FIXME take members into account */

  return memsize + GIMP_OBJECT_CLASS (parent_class)->get_memsize (object,
                                                                  gui_size);
}

150 151 152 153 154 155 156
static void
gimp_tagged_container_clear (GimpContainer *container)
{
  GimpFilteredContainer *filtered_container = GIMP_FILTERED_CONTAINER (container);
  GimpTaggedContainer   *tagged_container   = GIMP_TAGGED_CONTAINER (container);
  GList                 *list;

157
  for (list = GIMP_LIST (filtered_container->src_container)->queue->head;
158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177
       list;
       list = g_list_next (list))
    {
      g_signal_handlers_disconnect_by_func (list->data,
                                            gimp_tagged_container_tag_added,
                                            tagged_container);
      g_signal_handlers_disconnect_by_func (list->data,
                                            gimp_tagged_container_tag_removed,
                                            tagged_container);
    }

  if (tagged_container->tag_ref_counts)
    {
      g_hash_table_remove_all (tagged_container->tag_ref_counts);
      tagged_container->tag_count = 0;
    }

  GIMP_CONTAINER_CLASS (parent_class)->clear (container);
}

178
static void
179 180
gimp_tagged_container_src_add (GimpFilteredContainer *filtered_container,
                               GimpObject            *object)
181
{
182 183
  GimpTaggedContainer *tagged_container = GIMP_TAGGED_CONTAINER (filtered_container);
  GList               *list;
184

185 186 187
  for (list = gimp_tagged_get_tags (GIMP_TAGGED (object));
       list;
       list = g_list_next (list))
188
    {
189 190 191 192 193 194 195 196 197 198 199 200 201
      gimp_tagged_container_ref_tag (tagged_container, list->data);
    }

  g_signal_connect (object, "tag-added",
                    G_CALLBACK (gimp_tagged_container_tag_added),
                    tagged_container);
  g_signal_connect (object, "tag-removed",
                    G_CALLBACK (gimp_tagged_container_tag_removed),
                    tagged_container);

  if (gimp_tagged_container_object_matches (tagged_container, object))
    {
      gimp_container_add (GIMP_CONTAINER (tagged_container), object);
202 203 204 205
    }
}

static void
206 207
gimp_tagged_container_src_remove (GimpFilteredContainer *filtered_container,
                                  GimpObject            *object)
208
{
209 210
  GimpTaggedContainer *tagged_container = GIMP_TAGGED_CONTAINER (filtered_container);
  GList               *list;
211

212 213 214 215 216 217 218 219 220 221
  g_signal_handlers_disconnect_by_func (object,
                                        gimp_tagged_container_tag_added,
                                        tagged_container);
  g_signal_handlers_disconnect_by_func (object,
                                        gimp_tagged_container_tag_removed,
                                        tagged_container);

  for (list = gimp_tagged_get_tags (GIMP_TAGGED (object));
       list;
       list = g_list_next (list))
222
    {
223 224
      gimp_tagged_container_unref_tag (tagged_container, list->data);
    }
225

226 227 228
  if (gimp_tagged_container_object_matches (tagged_container, object))
    {
      gimp_container_remove (GIMP_CONTAINER (tagged_container), object);
229 230 231
    }
}

232 233
static void
gimp_tagged_container_src_freeze (GimpFilteredContainer *filtered_container)
234
{
235
  gimp_container_clear (GIMP_CONTAINER (filtered_container));
236
}
237

238 239 240 241 242
static void
gimp_tagged_container_src_thaw (GimpFilteredContainer *filtered_container)
{
  GList *list;

243
  for (list = GIMP_LIST (filtered_container->src_container)->queue->head;
244 245 246 247 248
       list;
       list = g_list_next (list))
    {
      gimp_tagged_container_src_add (filtered_container, list->data);
    }
249 250 251 252 253 254 255
}

/**
 * gimp_tagged_container_new:
 * @src_container: container to be filtered.
 *
 * Creates a new #GimpTaggedContainer object which creates filtered
256
 * data view of #GimpTagged objects. It filters @src_container for
257
 * objects containing all of the filtering tags. Synchronization with
258
 * @src_container data is performed automatically.
259 260 261 262
 *
 * Return value: a new #GimpTaggedContainer object.
 **/
GimpContainer *
263
gimp_tagged_container_new (GimpContainer *src_container)
264 265
{
  GimpTaggedContainer *tagged_container;
266 267
  GType                children_type;
  GCompareFunc         sort_func;
268

269
  g_return_val_if_fail (GIMP_IS_LIST (src_container), NULL);
270 271

  children_type = gimp_container_get_children_type (src_container);
272
  sort_func     = GIMP_LIST (src_container)->sort_func;
273 274 275 276 277 278 279 280 281 282 283 284 285 286 287

  tagged_container = g_object_new (GIMP_TYPE_TAGGED_CONTAINER,
                                   "sort-func",     sort_func,
                                   "children-type", children_type,
                                   "policy",        GIMP_CONTAINER_POLICY_WEAK,
                                   "unique-names",  FALSE,
                                   "src-container", src_container,
                                   NULL);

  return GIMP_CONTAINER (tagged_container);
}

/**
 * gimp_tagged_container_set_filter:
 * @tagged_container: a #GimpTaggedContainer object.
288
 * @tags:             list of #GimpTag objects.
289
 *
290 291
 * Sets list of tags to be used for filtering. Only objects which have
 * all of the tags assigned match filtering criteria.
292 293 294 295 296
 **/
void
gimp_tagged_container_set_filter (GimpTaggedContainer *tagged_container,
                                  GList               *tags)
{
297 298
  GList *new_filter;

299 300
  g_return_if_fail (GIMP_IS_TAGGED_CONTAINER (tagged_container));

301 302 303 304 305 306 307 308
  if (tags)
    {
      GList *list;

      for (list = tags; list; list = g_list_next (list))
        g_return_if_fail (list->data == NULL || GIMP_IS_TAG (list->data));
    }

309 310 311
  if (! gimp_container_frozen (GIMP_FILTERED_CONTAINER (tagged_container)->src_container))
    {
      gimp_tagged_container_src_freeze (GIMP_FILTERED_CONTAINER (tagged_container));
312
    }
313

314 315 316 317 318 319 320
  /*  ref new tags first, they could be the same as the old ones  */
  new_filter = g_list_copy (tags);
  g_list_foreach (new_filter, (GFunc) gimp_tag_or_null_ref, NULL);

  g_list_free_full (tagged_container->filter,
                    (GDestroyNotify) gimp_tag_or_null_unref);
  tagged_container->filter = new_filter;
321

322 323
  if (! gimp_container_frozen (GIMP_FILTERED_CONTAINER (tagged_container)->src_container))
    {
324 325
      gimp_tagged_container_src_thaw (GIMP_FILTERED_CONTAINER (tagged_container));
    }
326 327 328 329 330 331 332 333 334 335
}

/**
 * gimp_tagged_container_get_filter:
 * @tagged_container: a #GimpTaggedContainer object.
 *
 * Returns current tag filter. Tag filter is a list of GimpTag objects, which
 * must be contained by each object matching filter criteria.
 *
 * Return value: a list of GimpTag objects used as filter. This value should
336
 *               not be modified or freed.
337 338 339 340 341 342 343 344 345 346 347 348 349
 **/
const GList *
gimp_tagged_container_get_filter (GimpTaggedContainer *tagged_container)
{
  g_return_val_if_fail (GIMP_IS_TAGGED_CONTAINER (tagged_container), NULL);

  return tagged_container->filter;
}

static gboolean
gimp_tagged_container_object_matches (GimpTaggedContainer *tagged_container,
                                      GimpObject          *object)
{
350
  GList *filter_tags;
351

352 353 354
  for (filter_tags = tagged_container->filter;
       filter_tags;
       filter_tags = g_list_next (filter_tags))
355
    {
356
      if (! filter_tags->data)
357 358 359 360 361
        {
          /* invalid tag - does not match */
          return FALSE;
        }

362 363
      if (! gimp_tagged_has_tag (GIMP_TAGGED (object),
                                 filter_tags->data))
364 365
        {
          /* match for the tag was not found.
366 367
           * since query is of type AND, it whole fails.
           */
368 369 370 371 372 373 374 375
          return FALSE;
        }
    }

  return TRUE;
}

static void
376 377 378
gimp_tagged_container_tag_added (GimpTagged          *tagged,
                                 GimpTag             *tag,
                                 GimpTaggedContainer *tagged_container)
379
{
380
  gimp_tagged_container_ref_tag (tagged_container, tag);
381

382 383 384 385
  if (gimp_tagged_container_object_matches (tagged_container,
                                            GIMP_OBJECT (tagged)) &&
      ! gimp_container_have (GIMP_CONTAINER (tagged_container),
                             GIMP_OBJECT (tagged)))
386
    {
387 388
      gimp_container_add (GIMP_CONTAINER (tagged_container),
                          GIMP_OBJECT (tagged));
389 390 391 392
    }
}

static void
393 394 395
gimp_tagged_container_tag_removed (GimpTagged          *tagged,
                                   GimpTag             *tag,
                                   GimpTaggedContainer *tagged_container)
396
{
397
  gimp_tagged_container_unref_tag (tagged_container, tag);
398

399 400 401 402
  if (! gimp_tagged_container_object_matches (tagged_container,
                                              GIMP_OBJECT (tagged)) &&
      gimp_container_have (GIMP_CONTAINER (tagged_container),
                           GIMP_OBJECT (tagged)))
403
    {
404 405
      gimp_container_remove (GIMP_CONTAINER (tagged_container),
                             GIMP_OBJECT (tagged));
406 407 408 409
    }
}

static void
410 411
gimp_tagged_container_ref_tag (GimpTaggedContainer *tagged_container,
                               GimpTag             *tag)
412 413 414 415 416 417 418
{
  gint ref_count;

  ref_count = GPOINTER_TO_INT (g_hash_table_lookup (tagged_container->tag_ref_counts,
                                                    tag));
  ref_count++;
  g_hash_table_insert (tagged_container->tag_ref_counts,
419 420 421
                       g_object_ref (tag),
                       GINT_TO_POINTER (ref_count));

422 423 424 425 426 427 428 429 430 431
  if (ref_count == 1)
    {
      tagged_container->tag_count++;
      g_signal_emit (tagged_container,
                     gimp_tagged_container_signals[TAG_COUNT_CHANGED], 0,
                     tagged_container->tag_count);
    }
}

static void
432 433
gimp_tagged_container_unref_tag (GimpTaggedContainer *tagged_container,
                                 GimpTag             *tag)
434 435 436 437 438 439 440 441 442 443
{
  gint ref_count;

  ref_count = GPOINTER_TO_INT (g_hash_table_lookup (tagged_container->tag_ref_counts,
                                                    tag));
  ref_count--;

  if (ref_count > 0)
    {
      g_hash_table_insert (tagged_container->tag_ref_counts,
444 445
                           g_object_ref (tag),
                           GINT_TO_POINTER (ref_count));
446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483
    }
  else
    {
      if (g_hash_table_remove (tagged_container->tag_ref_counts, tag))
        {
          tagged_container->tag_count--;
          g_signal_emit (tagged_container,
                         gimp_tagged_container_signals[TAG_COUNT_CHANGED], 0,
                         tagged_container->tag_count);
        }
    }
}

static void
gimp_tagged_container_tag_count_changed (GimpTaggedContainer *container,
                                         gint                 tag_count)
{
}

/**
 * gimp_tagged_container_get_tag_count:
 * @container:  a #GimpTaggedContainer object.
 *
 * Get number of distinct tags that are currently assigned to all
 * objects in the container. The count is independent of currently
 * used filter, it is provided for all available objects (ie. empty
 * filter).
 *
 * Return value: number of distinct tags assigned to all objects in the
 *               container.
 **/
gint
gimp_tagged_container_get_tag_count (GimpTaggedContainer *container)
{
  g_return_val_if_fail (GIMP_IS_TAGGED_CONTAINER (container), 0);

  return container->tag_count;
}