1 /* GIMP - The GNU Image Manipulation Program
2 * Copyright (C) 1995 Spencer Kimball and Peter Mattis
3 *
4 * gimpcontainerview.c
5 * Copyright (C) 2001-2010 Michael Natterer <mitch@gimp.org>
6 *
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * 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 #include "config.h"
22
23 #include <string.h>
24
25 #include <gegl.h>
26 #include <gtk/gtk.h>
27
28 #include "libgimpwidgets/gimpwidgets.h"
29
30 #include "widgets-types.h"
31
32 #include "core/gimpcontainer.h"
33 #include "core/gimpcontext.h"
34 #include "core/gimpmarshal.h"
35 #include "core/gimptreehandler.h"
36 #include "core/gimpviewable.h"
37
38 #include "gimpcontainerview.h"
39 #include "gimpdnd.h"
40 #include "gimpviewrenderer.h"
41 #include "gimpuimanager.h"
42 #include "gimpcontainertreeview.h"
43
44
45 enum
46 {
47 SELECT_ITEM,
48 ACTIVATE_ITEM,
49 CONTEXT_ITEM,
50 LAST_SIGNAL
51 };
52
53
54 #define GIMP_CONTAINER_VIEW_GET_PRIVATE(obj) (gimp_container_view_get_private ((GimpContainerView *) (obj)))
55
56
57 typedef struct _GimpContainerViewPrivate GimpContainerViewPrivate;
58
59 struct _GimpContainerViewPrivate
60 {
61 GimpContainer *container;
62 GimpContext *context;
63
64 GHashTable *item_hash;
65
66 gint view_size;
67 gint view_border_width;
68 gboolean reorderable;
69 GtkSelectionMode selection_mode;
70
71 /* initialized by subclass */
72 GtkWidget *dnd_widget;
73
74 GimpTreeHandler *name_changed_handler;
75 GimpTreeHandler *expanded_changed_handler;
76 };
77
78
79 /* local function prototypes */
80
81 static GimpContainerViewPrivate *
82 gimp_container_view_get_private (GimpContainerView *view);
83
84 static void gimp_container_view_real_set_container (GimpContainerView *view,
85 GimpContainer *container);
86 static void gimp_container_view_real_set_context (GimpContainerView *view,
87 GimpContext *context);
88 static void gimp_container_view_real_set_selection_mode (GimpContainerView *view,
89 GtkSelectionMode mode);
90
91 static void gimp_container_view_clear_items (GimpContainerView *view);
92 static void gimp_container_view_real_clear_items (GimpContainerView *view);
93
94 static void gimp_container_view_add_container (GimpContainerView *view,
95 GimpContainer *container);
96 static void gimp_container_view_add_foreach (GimpViewable *viewable,
97 GimpContainerView *view);
98 static void gimp_container_view_add (GimpContainerView *view,
99 GimpViewable *viewable,
100 GimpContainer *container);
101
102 static void gimp_container_view_remove_container (GimpContainerView *view,
103 GimpContainer *container);
104 static void gimp_container_view_remove_foreach (GimpViewable *viewable,
105 GimpContainerView *view);
106 static void gimp_container_view_remove (GimpContainerView *view,
107 GimpViewable *viewable,
108 GimpContainer *container);
109
110 static void gimp_container_view_reorder (GimpContainerView *view,
111 GimpViewable *viewable,
112 gint new_index,
113 GimpContainer *container);
114
115 static void gimp_container_view_freeze (GimpContainerView *view,
116 GimpContainer *container);
117 static void gimp_container_view_thaw (GimpContainerView *view,
118 GimpContainer *container);
119 static void gimp_container_view_name_changed (GimpViewable *viewable,
120 GimpContainerView *view);
121 static void gimp_container_view_expanded_changed (GimpViewable *viewable,
122 GimpContainerView *view);
123
124 static void gimp_container_view_connect_context (GimpContainerView *view);
125 static void gimp_container_view_disconnect_context (GimpContainerView *view);
126
127 static void gimp_container_view_context_changed (GimpContext *context,
128 GimpViewable *viewable,
129 GimpContainerView *view);
130 static void gimp_container_view_viewable_dropped (GtkWidget *widget,
131 gint x,
132 gint y,
133 GimpViewable *viewable,
134 gpointer data);
135 static void gimp_container_view_button_viewable_dropped (GtkWidget *widget,
136 gint x,
137 gint y,
138 GimpViewable *viewable,
139 gpointer data);
140 static gint gimp_container_view_real_get_selected (GimpContainerView *view,
141 GList **list);
142
143
144 G_DEFINE_INTERFACE (GimpContainerView, gimp_container_view, GTK_TYPE_WIDGET)
145
146
147 static guint view_signals[LAST_SIGNAL] = { 0 };
148
149
150 static void
gimp_container_view_default_init(GimpContainerViewInterface * iface)151 gimp_container_view_default_init (GimpContainerViewInterface *iface)
152 {
153 view_signals[SELECT_ITEM] =
154 g_signal_new ("select-item",
155 G_TYPE_FROM_INTERFACE (iface),
156 G_SIGNAL_RUN_LAST,
157 G_STRUCT_OFFSET (GimpContainerViewInterface, select_item),
158 NULL, NULL,
159 gimp_marshal_BOOLEAN__OBJECT_POINTER,
160 G_TYPE_BOOLEAN, 2,
161 GIMP_TYPE_OBJECT,
162 G_TYPE_POINTER);
163
164 view_signals[ACTIVATE_ITEM] =
165 g_signal_new ("activate-item",
166 G_TYPE_FROM_INTERFACE (iface),
167 G_SIGNAL_RUN_FIRST,
168 G_STRUCT_OFFSET (GimpContainerViewInterface, activate_item),
169 NULL, NULL,
170 gimp_marshal_VOID__OBJECT_POINTER,
171 G_TYPE_NONE, 2,
172 GIMP_TYPE_OBJECT,
173 G_TYPE_POINTER);
174
175 view_signals[CONTEXT_ITEM] =
176 g_signal_new ("context-item",
177 G_TYPE_FROM_INTERFACE (iface),
178 G_SIGNAL_RUN_FIRST,
179 G_STRUCT_OFFSET (GimpContainerViewInterface, context_item),
180 NULL, NULL,
181 gimp_marshal_VOID__OBJECT_POINTER,
182 G_TYPE_NONE, 2,
183 GIMP_TYPE_OBJECT,
184 G_TYPE_POINTER);
185
186 iface->select_item = NULL;
187 iface->activate_item = NULL;
188 iface->context_item = NULL;
189
190 iface->set_container = gimp_container_view_real_set_container;
191 iface->set_context = gimp_container_view_real_set_context;
192 iface->set_selection_mode = gimp_container_view_real_set_selection_mode;
193 iface->insert_item = NULL;
194 iface->insert_item_after = NULL;
195 iface->remove_item = NULL;
196 iface->reorder_item = NULL;
197 iface->rename_item = NULL;
198 iface->expand_item = NULL;
199 iface->clear_items = gimp_container_view_real_clear_items;
200 iface->set_view_size = NULL;
201 iface->get_selected = gimp_container_view_real_get_selected;
202
203 iface->insert_data_free = NULL;
204 iface->model_is_tree = FALSE;
205
206 g_object_interface_install_property (iface,
207 g_param_spec_object ("container",
208 NULL, NULL,
209 GIMP_TYPE_CONTAINER,
210 GIMP_PARAM_READWRITE));
211
212 g_object_interface_install_property (iface,
213 g_param_spec_object ("context",
214 NULL, NULL,
215 GIMP_TYPE_CONTEXT,
216 GIMP_PARAM_READWRITE));
217
218 g_object_interface_install_property (iface,
219 g_param_spec_enum ("selection-mode",
220 NULL, NULL,
221 GTK_TYPE_SELECTION_MODE,
222 GTK_SELECTION_SINGLE,
223 GIMP_PARAM_READWRITE));
224
225 g_object_interface_install_property (iface,
226 g_param_spec_boolean ("reorderable",
227 NULL, NULL,
228 FALSE,
229 GIMP_PARAM_READWRITE));
230
231 g_object_interface_install_property (iface,
232 g_param_spec_int ("view-size",
233 NULL, NULL,
234 1, GIMP_VIEWABLE_MAX_PREVIEW_SIZE,
235 GIMP_VIEW_SIZE_MEDIUM,
236 GIMP_PARAM_READWRITE |
237 G_PARAM_CONSTRUCT));
238
239 g_object_interface_install_property (iface,
240 g_param_spec_int ("view-border-width",
241 NULL, NULL,
242 0,
243 GIMP_VIEW_MAX_BORDER_WIDTH,
244 1,
245 GIMP_PARAM_READWRITE |
246 G_PARAM_CONSTRUCT));
247 }
248
249 static void
gimp_container_view_private_dispose(GimpContainerView * view,GimpContainerViewPrivate * private)250 gimp_container_view_private_dispose (GimpContainerView *view,
251 GimpContainerViewPrivate *private)
252 {
253 if (private->container)
254 gimp_container_view_set_container (view, NULL);
255
256 if (private->context)
257 gimp_container_view_set_context (view, NULL);
258 }
259
260 static void
gimp_container_view_private_finalize(GimpContainerViewPrivate * private)261 gimp_container_view_private_finalize (GimpContainerViewPrivate *private)
262 {
263 if (private->item_hash)
264 {
265 g_hash_table_destroy (private->item_hash);
266 private->item_hash = NULL;
267 }
268 g_clear_pointer (&private->name_changed_handler,
269 gimp_tree_handler_disconnect);
270 g_clear_pointer (&private->expanded_changed_handler,
271 gimp_tree_handler_disconnect);
272
273 g_slice_free (GimpContainerViewPrivate, private);
274 }
275
276 static GimpContainerViewPrivate *
gimp_container_view_get_private(GimpContainerView * view)277 gimp_container_view_get_private (GimpContainerView *view)
278 {
279 GimpContainerViewPrivate *private;
280
281 static GQuark private_key = 0;
282
283 g_return_val_if_fail (GIMP_IS_CONTAINER_VIEW (view), NULL);
284
285 if (! private_key)
286 private_key = g_quark_from_static_string ("gimp-container-view-private");
287
288 private = g_object_get_qdata ((GObject *) view, private_key);
289
290 if (! private)
291 {
292 GimpContainerViewInterface *view_iface;
293
294 view_iface = GIMP_CONTAINER_VIEW_GET_INTERFACE (view);
295
296 private = g_slice_new0 (GimpContainerViewPrivate);
297
298 private->view_border_width = 1;
299
300 private->item_hash = g_hash_table_new_full (g_direct_hash,
301 g_direct_equal,
302 NULL,
303 view_iface->insert_data_free);
304
305 g_object_set_qdata_full ((GObject *) view, private_key, private,
306 (GDestroyNotify) gimp_container_view_private_finalize);
307
308 g_signal_connect (view, "destroy",
309 G_CALLBACK (gimp_container_view_private_dispose),
310 private);
311 }
312
313 return private;
314 }
315
316 /**
317 * gimp_container_view_install_properties:
318 * @klass: the class structure for a type deriving from #GObject
319 *
320 * Installs the necessary properties for a class implementing
321 * #GimpContainerView. A #GimpContainerViewProp property is installed
322 * for each property, using the values from the #GimpContainerViewProp
323 * enumeration. The caller must make sure itself that the enumeration
324 * values don't collide with some other property values they
325 * are using (that's what %GIMP_CONTAINER_VIEW_PROP_LAST is good for).
326 **/
327 void
gimp_container_view_install_properties(GObjectClass * klass)328 gimp_container_view_install_properties (GObjectClass *klass)
329 {
330 g_object_class_override_property (klass,
331 GIMP_CONTAINER_VIEW_PROP_CONTAINER,
332 "container");
333 g_object_class_override_property (klass,
334 GIMP_CONTAINER_VIEW_PROP_CONTEXT,
335 "context");
336 g_object_class_override_property (klass,
337 GIMP_CONTAINER_VIEW_PROP_SELECTION_MODE,
338 "selection-mode");
339 g_object_class_override_property (klass,
340 GIMP_CONTAINER_VIEW_PROP_REORDERABLE,
341 "reorderable");
342 g_object_class_override_property (klass,
343 GIMP_CONTAINER_VIEW_PROP_VIEW_SIZE,
344 "view-size");
345 g_object_class_override_property (klass,
346 GIMP_CONTAINER_VIEW_PROP_VIEW_BORDER_WIDTH,
347 "view-border-width");
348 }
349
350 GimpContainer *
gimp_container_view_get_container(GimpContainerView * view)351 gimp_container_view_get_container (GimpContainerView *view)
352 {
353 GimpContainerViewPrivate *private;
354
355 g_return_val_if_fail (GIMP_IS_CONTAINER_VIEW (view), NULL);
356
357 private = GIMP_CONTAINER_VIEW_GET_PRIVATE (view);
358
359 return private->container;
360 }
361
362 void
gimp_container_view_set_container(GimpContainerView * view,GimpContainer * container)363 gimp_container_view_set_container (GimpContainerView *view,
364 GimpContainer *container)
365 {
366 GimpContainerViewPrivate *private;
367
368 g_return_if_fail (GIMP_IS_CONTAINER_VIEW (view));
369 g_return_if_fail (container == NULL || GIMP_IS_CONTAINER (container));
370 if (container)
371 g_return_if_fail (g_type_is_a (gimp_container_get_children_type (container),
372 GIMP_TYPE_VIEWABLE));
373
374 private = GIMP_CONTAINER_VIEW_GET_PRIVATE (view);
375
376 if (container != private->container)
377 {
378 GIMP_CONTAINER_VIEW_GET_INTERFACE (view)->set_container (view, container);
379
380 g_object_notify (G_OBJECT (view), "container");
381 }
382 }
383
384 static void
gimp_container_view_real_set_container(GimpContainerView * view,GimpContainer * container)385 gimp_container_view_real_set_container (GimpContainerView *view,
386 GimpContainer *container)
387 {
388 GimpContainerViewPrivate *private = GIMP_CONTAINER_VIEW_GET_PRIVATE (view);
389
390 if (private->container)
391 {
392 if (private->context)
393 gimp_container_view_disconnect_context (view);
394
395 gimp_container_view_select_item (view, NULL);
396
397 /* freeze/thaw is only supported for the toplevel container */
398 g_signal_handlers_disconnect_by_func (private->container,
399 gimp_container_view_freeze,
400 view);
401 g_signal_handlers_disconnect_by_func (private->container,
402 gimp_container_view_thaw,
403 view);
404
405 if (! gimp_container_frozen (private->container))
406 gimp_container_view_remove_container (view, private->container);
407 }
408
409 private->container = container;
410
411 if (private->container)
412 {
413 if (! gimp_container_frozen (private->container))
414 gimp_container_view_add_container (view, private->container);
415
416 /* freeze/thaw is only supported for the toplevel container */
417 g_signal_connect_object (private->container, "freeze",
418 G_CALLBACK (gimp_container_view_freeze),
419 view,
420 G_CONNECT_SWAPPED);
421 g_signal_connect_object (private->container, "thaw",
422 G_CALLBACK (gimp_container_view_thaw),
423 view,
424 G_CONNECT_SWAPPED);
425
426 if (private->context)
427 gimp_container_view_connect_context (view);
428 }
429 }
430
431 GimpContext *
gimp_container_view_get_context(GimpContainerView * view)432 gimp_container_view_get_context (GimpContainerView *view)
433 {
434 GimpContainerViewPrivate *private;
435
436 g_return_val_if_fail (GIMP_IS_CONTAINER_VIEW (view), NULL);
437
438 private = GIMP_CONTAINER_VIEW_GET_PRIVATE (view);
439
440 return private->context;
441 }
442
443 void
gimp_container_view_set_context(GimpContainerView * view,GimpContext * context)444 gimp_container_view_set_context (GimpContainerView *view,
445 GimpContext *context)
446 {
447 GimpContainerViewPrivate *private;
448
449 g_return_if_fail (GIMP_IS_CONTAINER_VIEW (view));
450 g_return_if_fail (context == NULL || GIMP_IS_CONTEXT (context));
451
452 private = GIMP_CONTAINER_VIEW_GET_PRIVATE (view);
453
454 if (context != private->context)
455 {
456 GIMP_CONTAINER_VIEW_GET_INTERFACE (view)->set_context (view, context);
457
458 g_object_notify (G_OBJECT (view), "context");
459 }
460 }
461
462 static void
gimp_container_view_real_set_context(GimpContainerView * view,GimpContext * context)463 gimp_container_view_real_set_context (GimpContainerView *view,
464 GimpContext *context)
465 {
466 GimpContainerViewPrivate *private = GIMP_CONTAINER_VIEW_GET_PRIVATE (view);
467
468 if (private->context &&
469 private->container)
470 {
471 gimp_container_view_disconnect_context (view);
472 }
473
474 g_set_object (&private->context, context);
475
476 if (private->context &&
477 private->container)
478 {
479 gimp_container_view_connect_context (view);
480 }
481 }
482
483 GtkSelectionMode
gimp_container_view_get_selection_mode(GimpContainerView * view)484 gimp_container_view_get_selection_mode (GimpContainerView *view)
485 {
486 GimpContainerViewPrivate *private = GIMP_CONTAINER_VIEW_GET_PRIVATE (view);
487
488 return private->selection_mode;
489 }
490
491 void
gimp_container_view_set_selection_mode(GimpContainerView * view,GtkSelectionMode mode)492 gimp_container_view_set_selection_mode (GimpContainerView *view,
493 GtkSelectionMode mode)
494 {
495 g_return_if_fail (GIMP_IS_CONTAINER_VIEW (view));
496 g_return_if_fail (mode == GTK_SELECTION_SINGLE ||
497 mode == GTK_SELECTION_MULTIPLE);
498
499 GIMP_CONTAINER_VIEW_GET_INTERFACE (view)->set_selection_mode (view, mode);
500 }
501
502 static void
gimp_container_view_real_set_selection_mode(GimpContainerView * view,GtkSelectionMode mode)503 gimp_container_view_real_set_selection_mode (GimpContainerView *view,
504 GtkSelectionMode mode)
505 {
506 GimpContainerViewPrivate *private = GIMP_CONTAINER_VIEW_GET_PRIVATE (view);
507
508 private->selection_mode = mode;
509 }
510
511 gint
gimp_container_view_get_view_size(GimpContainerView * view,gint * view_border_width)512 gimp_container_view_get_view_size (GimpContainerView *view,
513 gint *view_border_width)
514 {
515 GimpContainerViewPrivate *private;
516
517 g_return_val_if_fail (GIMP_IS_CONTAINER_VIEW (view), 0);
518
519 private = GIMP_CONTAINER_VIEW_GET_PRIVATE (view);
520
521 if (view_border_width)
522 *view_border_width = private->view_border_width;
523
524 return private->view_size;
525 }
526
527 void
gimp_container_view_set_view_size(GimpContainerView * view,gint view_size,gint view_border_width)528 gimp_container_view_set_view_size (GimpContainerView *view,
529 gint view_size,
530 gint view_border_width)
531 {
532 GimpContainerViewPrivate *private;
533
534 g_return_if_fail (GIMP_IS_CONTAINER_VIEW (view));
535 g_return_if_fail (view_size > 0 &&
536 view_size <= GIMP_VIEWABLE_MAX_PREVIEW_SIZE);
537 g_return_if_fail (view_border_width >= 0 &&
538 view_border_width <= GIMP_VIEW_MAX_BORDER_WIDTH);
539
540 private = GIMP_CONTAINER_VIEW_GET_PRIVATE (view);
541
542 if (private->view_size != view_size ||
543 private->view_border_width != view_border_width)
544 {
545 private->view_size = view_size;
546 private->view_border_width = view_border_width;
547
548 GIMP_CONTAINER_VIEW_GET_INTERFACE (view)->set_view_size (view);
549
550 g_object_freeze_notify (G_OBJECT (view));
551 g_object_notify (G_OBJECT (view), "view-size");
552 g_object_notify (G_OBJECT (view), "view-border-width");
553 g_object_thaw_notify (G_OBJECT (view));
554 }
555 }
556
557 gboolean
gimp_container_view_get_reorderable(GimpContainerView * view)558 gimp_container_view_get_reorderable (GimpContainerView *view)
559 {
560 GimpContainerViewPrivate *private;
561
562 g_return_val_if_fail (GIMP_IS_CONTAINER_VIEW (view), FALSE);
563
564 private = GIMP_CONTAINER_VIEW_GET_PRIVATE (view);
565
566 return private->reorderable;
567 }
568
569 void
gimp_container_view_set_reorderable(GimpContainerView * view,gboolean reorderable)570 gimp_container_view_set_reorderable (GimpContainerView *view,
571 gboolean reorderable)
572 {
573 GimpContainerViewPrivate *private;
574
575 g_return_if_fail (GIMP_IS_CONTAINER_VIEW (view));
576
577 private = GIMP_CONTAINER_VIEW_GET_PRIVATE (view);
578
579 private->reorderable = reorderable ? TRUE : FALSE;
580 g_object_notify (G_OBJECT (view), "reorderable");
581 }
582
583 GtkWidget *
gimp_container_view_get_dnd_widget(GimpContainerView * view)584 gimp_container_view_get_dnd_widget (GimpContainerView *view)
585 {
586 GimpContainerViewPrivate *private;
587
588 g_return_val_if_fail (GIMP_IS_CONTAINER_VIEW (view), NULL);
589
590 private = GIMP_CONTAINER_VIEW_GET_PRIVATE (view);
591
592 return private->dnd_widget;
593 }
594
595 void
gimp_container_view_set_dnd_widget(GimpContainerView * view,GtkWidget * dnd_widget)596 gimp_container_view_set_dnd_widget (GimpContainerView *view,
597 GtkWidget *dnd_widget)
598 {
599 GimpContainerViewPrivate *private;
600
601 g_return_if_fail (GIMP_IS_CONTAINER_VIEW (view));
602 g_return_if_fail (dnd_widget == NULL || GTK_IS_WIDGET (dnd_widget));
603
604 private = GIMP_CONTAINER_VIEW_GET_PRIVATE (view);
605
606 private->dnd_widget = dnd_widget;
607 }
608
609 void
gimp_container_view_enable_dnd(GimpContainerView * view,GtkButton * button,GType children_type)610 gimp_container_view_enable_dnd (GimpContainerView *view,
611 GtkButton *button,
612 GType children_type)
613 {
614 g_return_if_fail (GIMP_IS_CONTAINER_VIEW (view));
615 g_return_if_fail (GTK_IS_BUTTON (button));
616
617 gimp_dnd_viewable_dest_add (GTK_WIDGET (button),
618 children_type,
619 gimp_container_view_button_viewable_dropped,
620 view);
621 }
622
623 gboolean
gimp_container_view_select_item(GimpContainerView * view,GimpViewable * viewable)624 gimp_container_view_select_item (GimpContainerView *view,
625 GimpViewable *viewable)
626 {
627 GimpContainerViewPrivate *private;
628 gboolean success = FALSE;
629 gpointer insert_data;
630
631 g_return_val_if_fail (GIMP_IS_CONTAINER_VIEW (view), FALSE);
632 g_return_val_if_fail (viewable == NULL || GIMP_IS_VIEWABLE (viewable), FALSE);
633
634 private = GIMP_CONTAINER_VIEW_GET_PRIVATE (view);
635
636 if (gimp_container_frozen (private->container))
637 return TRUE;
638
639 insert_data = g_hash_table_lookup (private->item_hash, viewable);
640
641 g_signal_emit (view, view_signals[SELECT_ITEM], 0,
642 viewable, insert_data, &success);
643
644 return success;
645 }
646
647 void
gimp_container_view_activate_item(GimpContainerView * view,GimpViewable * viewable)648 gimp_container_view_activate_item (GimpContainerView *view,
649 GimpViewable *viewable)
650 {
651 GimpContainerViewPrivate *private;
652 gpointer insert_data;
653
654 g_return_if_fail (GIMP_IS_CONTAINER_VIEW (view));
655 g_return_if_fail (GIMP_IS_VIEWABLE (viewable));
656
657 private = GIMP_CONTAINER_VIEW_GET_PRIVATE (view);
658
659 if (gimp_container_frozen (private->container))
660 return;
661
662 insert_data = g_hash_table_lookup (private->item_hash, viewable);
663
664 g_signal_emit (view, view_signals[ACTIVATE_ITEM], 0,
665 viewable, insert_data);
666 }
667
668 void
gimp_container_view_context_item(GimpContainerView * view,GimpViewable * viewable)669 gimp_container_view_context_item (GimpContainerView *view,
670 GimpViewable *viewable)
671 {
672 GimpContainerViewPrivate *private;
673 gpointer insert_data;
674
675 g_return_if_fail (GIMP_IS_CONTAINER_VIEW (view));
676 g_return_if_fail (GIMP_IS_VIEWABLE (viewable));
677
678 private = GIMP_CONTAINER_VIEW_GET_PRIVATE (view);
679
680 if (gimp_container_frozen (private->container))
681 return;
682
683 insert_data = g_hash_table_lookup (private->item_hash, viewable);
684
685 g_signal_emit (view, view_signals[CONTEXT_ITEM], 0,
686 viewable, insert_data);
687 }
688
689 gpointer
gimp_container_view_lookup(GimpContainerView * view,GimpViewable * viewable)690 gimp_container_view_lookup (GimpContainerView *view,
691 GimpViewable *viewable)
692 {
693 GimpContainerViewPrivate *private;
694
695 g_return_val_if_fail (GIMP_IS_CONTAINER_VIEW (view), NULL);
696 g_return_val_if_fail (viewable == NULL || GIMP_IS_VIEWABLE (viewable), NULL);
697
698 /* we handle the NULL viewable here as a workaround for bug #149906 */
699 if (! viewable)
700 return NULL;
701
702 private = GIMP_CONTAINER_VIEW_GET_PRIVATE (view);
703
704 return g_hash_table_lookup (private->item_hash, viewable);
705 }
706
707 gboolean
gimp_container_view_item_selected(GimpContainerView * view,GimpViewable * viewable)708 gimp_container_view_item_selected (GimpContainerView *view,
709 GimpViewable *viewable)
710 {
711 GimpContainerViewPrivate *private;
712 gboolean success;
713
714 g_return_val_if_fail (GIMP_IS_CONTAINER_VIEW (view), FALSE);
715 g_return_val_if_fail (GIMP_IS_VIEWABLE (viewable), FALSE);
716
717 private = GIMP_CONTAINER_VIEW_GET_PRIVATE (view);
718
719 /* HACK */
720 if (private->container && private->context)
721 {
722 GType children_type;
723 const gchar *signal_name;
724
725 children_type = gimp_container_get_children_type (private->container);
726 signal_name = gimp_context_type_to_signal_name (children_type);
727
728 if (signal_name)
729 {
730 gimp_context_set_by_type (private->context, children_type,
731 GIMP_OBJECT (viewable));
732 return TRUE;
733 }
734 }
735
736 success = gimp_container_view_select_item (view, viewable);
737
738 #if 0
739 if (success && private->container && private->context)
740 {
741 GimpContext *context;
742 GType children_type;
743
744 /* ref and remember the context because private->context may
745 * become NULL by calling gimp_context_set_by_type()
746 */
747 context = g_object_ref (private->context);
748 children_type = gimp_container_get_children_type (private->container);
749
750 g_signal_handlers_block_by_func (context,
751 gimp_container_view_context_changed,
752 view);
753
754 gimp_context_set_by_type (context, children_type, GIMP_OBJECT (viewable));
755
756 g_signal_handlers_unblock_by_func (context,
757 gimp_container_view_context_changed,
758 view);
759
760 g_object_unref (context);
761 }
762 #endif
763
764 return success;
765 }
766
767 gboolean
gimp_container_view_multi_selected(GimpContainerView * view,GList * items)768 gimp_container_view_multi_selected (GimpContainerView *view,
769 GList *items)
770 {
771 guint selected_count;
772 gboolean success = FALSE;
773
774 g_return_val_if_fail (GIMP_IS_CONTAINER_VIEW (view), FALSE);
775
776 selected_count = g_list_length (items);
777
778 if (selected_count == 0)
779 {
780 /* do nothing */
781 }
782 else if (selected_count == 1)
783 {
784 success = gimp_container_view_item_selected (view, items->data);
785 }
786 else
787 {
788 success = FALSE;
789 g_signal_emit (view, view_signals[SELECT_ITEM], 0,
790 NULL, items, &success);
791 }
792
793 return success;
794 }
795
796 gint
gimp_container_view_get_selected(GimpContainerView * view,GList ** list)797 gimp_container_view_get_selected (GimpContainerView *view,
798 GList **list)
799 {
800 g_return_val_if_fail (GIMP_IS_CONTAINER_VIEW (view), 0);
801
802 return GIMP_CONTAINER_VIEW_GET_INTERFACE (view)->get_selected (view, list);
803 }
804
805 static gint
gimp_container_view_real_get_selected(GimpContainerView * view,GList ** list)806 gimp_container_view_real_get_selected (GimpContainerView *view,
807 GList **list)
808 {
809 GimpContainerViewPrivate *private = GIMP_CONTAINER_VIEW_GET_PRIVATE (view);
810 GType children_type;
811 GimpObject *object;
812
813 if (list)
814 *list = NULL;
815
816 if (! private->container || ! private->context)
817 return 0;
818
819 children_type = gimp_container_get_children_type (private->container);
820 object = gimp_context_get_by_type (private->context,
821 children_type);
822
823 if (list && object)
824 *list = g_list_append (*list, object);
825
826 return object ? 1 : 0;
827 }
828
829 void
gimp_container_view_item_activated(GimpContainerView * view,GimpViewable * viewable)830 gimp_container_view_item_activated (GimpContainerView *view,
831 GimpViewable *viewable)
832 {
833 g_return_if_fail (GIMP_IS_CONTAINER_VIEW (view));
834 g_return_if_fail (GIMP_IS_VIEWABLE (viewable));
835
836 gimp_container_view_activate_item (view, viewable);
837 }
838
839 void
gimp_container_view_item_context(GimpContainerView * view,GimpViewable * viewable)840 gimp_container_view_item_context (GimpContainerView *view,
841 GimpViewable *viewable)
842 {
843 g_return_if_fail (GIMP_IS_CONTAINER_VIEW (view));
844 g_return_if_fail (GIMP_IS_VIEWABLE (viewable));
845
846 gimp_container_view_context_item (view, viewable);
847 }
848
849 void
gimp_container_view_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)850 gimp_container_view_set_property (GObject *object,
851 guint property_id,
852 const GValue *value,
853 GParamSpec *pspec)
854 {
855 GimpContainerView *view = GIMP_CONTAINER_VIEW (object);
856
857 switch (property_id)
858 {
859 case GIMP_CONTAINER_VIEW_PROP_CONTAINER:
860 gimp_container_view_set_container (view, g_value_get_object (value));
861 break;
862 case GIMP_CONTAINER_VIEW_PROP_CONTEXT:
863 gimp_container_view_set_context (view, g_value_get_object (value));
864 break;
865 case GIMP_CONTAINER_VIEW_PROP_SELECTION_MODE:
866 gimp_container_view_set_selection_mode (view, g_value_get_enum (value));
867 break;
868 case GIMP_CONTAINER_VIEW_PROP_REORDERABLE:
869 gimp_container_view_set_reorderable (view, g_value_get_boolean (value));
870 break;
871 case GIMP_CONTAINER_VIEW_PROP_VIEW_SIZE:
872 case GIMP_CONTAINER_VIEW_PROP_VIEW_BORDER_WIDTH:
873 {
874 gint size, border;
875
876 size = gimp_container_view_get_view_size (view, &border);
877
878 if (property_id == GIMP_CONTAINER_VIEW_PROP_VIEW_SIZE)
879 size = g_value_get_int (value);
880 else
881 border = g_value_get_int (value);
882
883 gimp_container_view_set_view_size (view, size, border);
884 }
885 break;
886 default:
887 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
888 break;
889 }
890 }
891
892 void
gimp_container_view_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)893 gimp_container_view_get_property (GObject *object,
894 guint property_id,
895 GValue *value,
896 GParamSpec *pspec)
897 {
898 GimpContainerView *view = GIMP_CONTAINER_VIEW (object);
899
900 switch (property_id)
901 {
902 case GIMP_CONTAINER_VIEW_PROP_CONTAINER:
903 g_value_set_object (value, gimp_container_view_get_container (view));
904 break;
905 case GIMP_CONTAINER_VIEW_PROP_CONTEXT:
906 g_value_set_object (value, gimp_container_view_get_context (view));
907 break;
908 case GIMP_CONTAINER_VIEW_PROP_SELECTION_MODE:
909 g_value_set_enum (value, gimp_container_view_get_selection_mode (view));
910 break;
911 case GIMP_CONTAINER_VIEW_PROP_REORDERABLE:
912 g_value_set_boolean (value, gimp_container_view_get_reorderable (view));
913 break;
914 case GIMP_CONTAINER_VIEW_PROP_VIEW_SIZE:
915 case GIMP_CONTAINER_VIEW_PROP_VIEW_BORDER_WIDTH:
916 {
917 gint size, border;
918
919 size = gimp_container_view_get_view_size (view, &border);
920
921 if (property_id == GIMP_CONTAINER_VIEW_PROP_VIEW_SIZE)
922 g_value_set_int (value, size);
923 else
924 g_value_set_int (value, border);
925 }
926 break;
927 default:
928 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
929 break;
930 }
931 }
932
933 static void
gimp_container_view_clear_items(GimpContainerView * view)934 gimp_container_view_clear_items (GimpContainerView *view)
935 {
936 GIMP_CONTAINER_VIEW_GET_INTERFACE (view)->clear_items (view);
937 }
938
939 static void
gimp_container_view_real_clear_items(GimpContainerView * view)940 gimp_container_view_real_clear_items (GimpContainerView *view)
941 {
942 GimpContainerViewPrivate *private = GIMP_CONTAINER_VIEW_GET_PRIVATE (view);
943
944 g_hash_table_remove_all (private->item_hash);
945 }
946
947 static void
gimp_container_view_add_container(GimpContainerView * view,GimpContainer * container)948 gimp_container_view_add_container (GimpContainerView *view,
949 GimpContainer *container)
950 {
951 GimpContainerViewPrivate *private = GIMP_CONTAINER_VIEW_GET_PRIVATE (view);
952
953 gimp_container_foreach (container,
954 (GFunc) gimp_container_view_add_foreach,
955 view);
956
957 if (container == private->container)
958 {
959 GType children_type;
960 GimpViewableClass *viewable_class;
961
962 children_type = gimp_container_get_children_type (container);
963 viewable_class = g_type_class_ref (children_type);
964
965 private->name_changed_handler =
966 gimp_tree_handler_connect (container,
967 viewable_class->name_changed_signal,
968 G_CALLBACK (gimp_container_view_name_changed),
969 view);
970
971 if (GIMP_CONTAINER_VIEW_GET_INTERFACE (view)->expand_item)
972 {
973 private->expanded_changed_handler =
974 gimp_tree_handler_connect (container,
975 "expanded-changed",
976 G_CALLBACK (gimp_container_view_expanded_changed),
977 view);
978 }
979
980 g_type_class_unref (viewable_class);
981 }
982
983 g_signal_connect_object (container, "add",
984 G_CALLBACK (gimp_container_view_add),
985 view,
986 G_CONNECT_SWAPPED);
987 g_signal_connect_object (container, "remove",
988 G_CALLBACK (gimp_container_view_remove),
989 view,
990 G_CONNECT_SWAPPED);
991 g_signal_connect_object (container, "reorder",
992 G_CALLBACK (gimp_container_view_reorder),
993 view,
994 G_CONNECT_SWAPPED);
995 }
996
997 static void
gimp_container_view_add_foreach(GimpViewable * viewable,GimpContainerView * view)998 gimp_container_view_add_foreach (GimpViewable *viewable,
999 GimpContainerView *view)
1000 {
1001 GimpContainerViewInterface *view_iface;
1002 GimpContainerViewPrivate *private;
1003 GimpViewable *parent;
1004 GimpContainer *children;
1005 gpointer parent_insert_data = NULL;
1006 gpointer insert_data;
1007
1008 view_iface = GIMP_CONTAINER_VIEW_GET_INTERFACE (view);
1009 private = GIMP_CONTAINER_VIEW_GET_PRIVATE (view);
1010
1011 parent = gimp_viewable_get_parent (viewable);
1012
1013 if (parent)
1014 parent_insert_data = g_hash_table_lookup (private->item_hash, parent);
1015
1016 insert_data = view_iface->insert_item (view, viewable,
1017 parent_insert_data, -1);
1018
1019 g_hash_table_insert (private->item_hash, viewable, insert_data);
1020
1021 if (view_iface->insert_item_after)
1022 view_iface->insert_item_after (view, viewable, insert_data);
1023
1024 children = gimp_viewable_get_children (viewable);
1025
1026 if (children)
1027 gimp_container_view_add_container (view, children);
1028 }
1029
1030 static void
gimp_container_view_add(GimpContainerView * view,GimpViewable * viewable,GimpContainer * container)1031 gimp_container_view_add (GimpContainerView *view,
1032 GimpViewable *viewable,
1033 GimpContainer *container)
1034 {
1035 GimpContainerViewInterface *view_iface;
1036 GimpContainerViewPrivate *private;
1037 GimpViewable *parent;
1038 GimpContainer *children;
1039 gpointer parent_insert_data = NULL;
1040 gpointer insert_data;
1041 gint index;
1042
1043 view_iface = GIMP_CONTAINER_VIEW_GET_INTERFACE (view);
1044 private = GIMP_CONTAINER_VIEW_GET_PRIVATE (view);
1045
1046 index = gimp_container_get_child_index (container,
1047 GIMP_OBJECT (viewable));
1048
1049 parent = gimp_viewable_get_parent (viewable);
1050
1051 if (parent)
1052 parent_insert_data = g_hash_table_lookup (private->item_hash, parent);
1053
1054 insert_data = view_iface->insert_item (view, viewable,
1055 parent_insert_data, index);
1056
1057 g_hash_table_insert (private->item_hash, viewable, insert_data);
1058
1059 if (view_iface->insert_item_after)
1060 view_iface->insert_item_after (view, viewable, insert_data);
1061
1062 children = gimp_viewable_get_children (viewable);
1063
1064 if (children)
1065 gimp_container_view_add_container (view, children);
1066 }
1067
1068 static void
gimp_container_view_remove_container(GimpContainerView * view,GimpContainer * container)1069 gimp_container_view_remove_container (GimpContainerView *view,
1070 GimpContainer *container)
1071 {
1072 GimpContainerViewPrivate *private = GIMP_CONTAINER_VIEW_GET_PRIVATE (view);
1073
1074 g_object_ref (container);
1075
1076 g_signal_handlers_disconnect_by_func (container,
1077 gimp_container_view_add,
1078 view);
1079 g_signal_handlers_disconnect_by_func (container,
1080 gimp_container_view_remove,
1081 view);
1082 g_signal_handlers_disconnect_by_func (container,
1083 gimp_container_view_reorder,
1084 view);
1085
1086 if (container == private->container)
1087 {
1088 g_clear_pointer (&private->name_changed_handler,
1089 gimp_tree_handler_disconnect);
1090 g_clear_pointer (&private->expanded_changed_handler,
1091 gimp_tree_handler_disconnect);
1092
1093 /* optimization: when the toplevel container gets removed, call
1094 * clear_items() which will get rid of all view widget stuff
1095 * *and* empty private->item_hash, so below call to
1096 * remove_foreach() will only disconnect all containers but not
1097 * remove all items individually (because they are gone from
1098 * item_hash).
1099 */
1100 gimp_container_view_clear_items (view);
1101 }
1102
1103 gimp_container_foreach (container,
1104 (GFunc) gimp_container_view_remove_foreach,
1105 view);
1106
1107 g_object_unref (container);
1108 }
1109
1110 static void
gimp_container_view_remove_foreach(GimpViewable * viewable,GimpContainerView * view)1111 gimp_container_view_remove_foreach (GimpViewable *viewable,
1112 GimpContainerView *view)
1113 {
1114 gimp_container_view_remove (view, viewable, NULL);
1115 }
1116
1117 static void
gimp_container_view_remove(GimpContainerView * view,GimpViewable * viewable,GimpContainer * unused)1118 gimp_container_view_remove (GimpContainerView *view,
1119 GimpViewable *viewable,
1120 GimpContainer *unused)
1121 {
1122 GimpContainerViewPrivate *private = GIMP_CONTAINER_VIEW_GET_PRIVATE (view);
1123 GimpContainer *children;
1124 gpointer insert_data;
1125
1126 children = gimp_viewable_get_children (viewable);
1127
1128 if (children)
1129 gimp_container_view_remove_container (view, children);
1130
1131 insert_data = g_hash_table_lookup (private->item_hash, viewable);
1132
1133 if (insert_data)
1134 {
1135 GIMP_CONTAINER_VIEW_GET_INTERFACE (view)->remove_item (view,
1136 viewable,
1137 insert_data);
1138
1139 g_hash_table_remove (private->item_hash, viewable);
1140 }
1141 }
1142
1143 static void
gimp_container_view_reorder(GimpContainerView * view,GimpViewable * viewable,gint new_index,GimpContainer * container)1144 gimp_container_view_reorder (GimpContainerView *view,
1145 GimpViewable *viewable,
1146 gint new_index,
1147 GimpContainer *container)
1148 {
1149 GimpContainerViewPrivate *private = GIMP_CONTAINER_VIEW_GET_PRIVATE (view);
1150 gpointer insert_data;
1151
1152 insert_data = g_hash_table_lookup (private->item_hash, viewable);
1153
1154 if (insert_data)
1155 {
1156 GIMP_CONTAINER_VIEW_GET_INTERFACE (view)->reorder_item (view,
1157 viewable,
1158 new_index,
1159 insert_data);
1160 }
1161 }
1162
1163 static void
gimp_container_view_freeze(GimpContainerView * view,GimpContainer * container)1164 gimp_container_view_freeze (GimpContainerView *view,
1165 GimpContainer *container)
1166 {
1167 gimp_container_view_remove_container (view, container);
1168 }
1169
1170 static void
gimp_container_view_thaw(GimpContainerView * view,GimpContainer * container)1171 gimp_container_view_thaw (GimpContainerView *view,
1172 GimpContainer *container)
1173 {
1174 GimpContainerViewPrivate *private = GIMP_CONTAINER_VIEW_GET_PRIVATE (view);
1175
1176 gimp_container_view_add_container (view, container);
1177
1178 if (private->context)
1179 {
1180 GType children_type;
1181 const gchar *signal_name;
1182
1183 children_type = gimp_container_get_children_type (private->container);
1184 signal_name = gimp_context_type_to_signal_name (children_type);
1185
1186 if (signal_name)
1187 {
1188 GimpObject *object;
1189
1190 object = gimp_context_get_by_type (private->context, children_type);
1191
1192 gimp_container_view_select_item (view, GIMP_VIEWABLE (object));
1193 }
1194 }
1195 }
1196
1197 static void
gimp_container_view_name_changed(GimpViewable * viewable,GimpContainerView * view)1198 gimp_container_view_name_changed (GimpViewable *viewable,
1199 GimpContainerView *view)
1200 {
1201 GimpContainerViewPrivate *private = GIMP_CONTAINER_VIEW_GET_PRIVATE (view);
1202 gpointer insert_data;
1203
1204 insert_data = g_hash_table_lookup (private->item_hash, viewable);
1205
1206 if (insert_data)
1207 {
1208 GIMP_CONTAINER_VIEW_GET_INTERFACE (view)->rename_item (view,
1209 viewable,
1210 insert_data);
1211 }
1212 }
1213
1214 static void
gimp_container_view_expanded_changed(GimpViewable * viewable,GimpContainerView * view)1215 gimp_container_view_expanded_changed (GimpViewable *viewable,
1216 GimpContainerView *view)
1217 {
1218 GimpContainerViewPrivate *private = GIMP_CONTAINER_VIEW_GET_PRIVATE (view);
1219 gpointer insert_data;
1220
1221 insert_data = g_hash_table_lookup (private->item_hash, viewable);
1222
1223 if (insert_data)
1224 {
1225 GIMP_CONTAINER_VIEW_GET_INTERFACE (view)->expand_item (view,
1226 viewable,
1227 insert_data);
1228 }
1229 }
1230
1231 static void
gimp_container_view_connect_context(GimpContainerView * view)1232 gimp_container_view_connect_context (GimpContainerView *view)
1233 {
1234 GimpContainerViewPrivate *private = GIMP_CONTAINER_VIEW_GET_PRIVATE (view);
1235 GType children_type;
1236 const gchar *signal_name;
1237
1238 children_type = gimp_container_get_children_type (private->container);
1239 signal_name = gimp_context_type_to_signal_name (children_type);
1240
1241 if (signal_name)
1242 {
1243 g_signal_connect_object (private->context, signal_name,
1244 G_CALLBACK (gimp_container_view_context_changed),
1245 view,
1246 0);
1247
1248 if (private->dnd_widget)
1249 gimp_dnd_viewable_dest_add (private->dnd_widget,
1250 children_type,
1251 gimp_container_view_viewable_dropped,
1252 view);
1253
1254 if (! gimp_container_frozen (private->container))
1255 {
1256 GimpObject *object = gimp_context_get_by_type (private->context,
1257 children_type);
1258
1259 gimp_container_view_select_item (view, GIMP_VIEWABLE (object));
1260 }
1261 }
1262 }
1263
1264 static void
gimp_container_view_disconnect_context(GimpContainerView * view)1265 gimp_container_view_disconnect_context (GimpContainerView *view)
1266 {
1267 GimpContainerViewPrivate *private = GIMP_CONTAINER_VIEW_GET_PRIVATE (view);
1268 GType children_type;
1269 const gchar *signal_name;
1270
1271 children_type = gimp_container_get_children_type (private->container);
1272 signal_name = gimp_context_type_to_signal_name (children_type);
1273
1274 if (signal_name)
1275 {
1276 g_signal_handlers_disconnect_by_func (private->context,
1277 gimp_container_view_context_changed,
1278 view);
1279
1280 if (private->dnd_widget)
1281 {
1282 gtk_drag_dest_unset (private->dnd_widget);
1283 gimp_dnd_viewable_dest_remove (private->dnd_widget,
1284 children_type);
1285 }
1286 }
1287 }
1288
1289 static void
gimp_container_view_context_changed(GimpContext * context,GimpViewable * viewable,GimpContainerView * view)1290 gimp_container_view_context_changed (GimpContext *context,
1291 GimpViewable *viewable,
1292 GimpContainerView *view)
1293 {
1294 if (! gimp_container_view_select_item (view, viewable))
1295 g_warning ("%s: select_item() failed (should not happen)", G_STRFUNC);
1296 }
1297
1298 static void
gimp_container_view_viewable_dropped(GtkWidget * widget,gint x,gint y,GimpViewable * viewable,gpointer data)1299 gimp_container_view_viewable_dropped (GtkWidget *widget,
1300 gint x,
1301 gint y,
1302 GimpViewable *viewable,
1303 gpointer data)
1304 {
1305 GimpContainerView *view = GIMP_CONTAINER_VIEW (data);
1306 GimpContainerViewPrivate *private = GIMP_CONTAINER_VIEW_GET_PRIVATE (view);
1307
1308 if (viewable && private->container &&
1309 gimp_container_have (private->container, GIMP_OBJECT (viewable)))
1310 {
1311 gimp_container_view_item_selected (view, viewable);
1312 }
1313 }
1314
1315 static void
gimp_container_view_button_viewable_dropped(GtkWidget * widget,gint x,gint y,GimpViewable * viewable,gpointer data)1316 gimp_container_view_button_viewable_dropped (GtkWidget *widget,
1317 gint x,
1318 gint y,
1319 GimpViewable *viewable,
1320 gpointer data)
1321 {
1322 GimpContainerView *view = GIMP_CONTAINER_VIEW (data);
1323
1324 if (viewable && gimp_container_view_lookup (view, viewable))
1325 {
1326 gimp_container_view_item_selected (view, viewable);
1327
1328 gtk_button_clicked (GTK_BUTTON (widget));
1329 }
1330 }
1331
1332