1 /* GIMP - The GNU Image Manipulation Program
2 * Copyright (C) 1995 Spencer Kimball and Peter Mattis
3 *
4 * gimpcomponenteditor.c
5 * Copyright (C) 2003-2005 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 <gegl.h>
24 #include <gtk/gtk.h>
25
26 #include "libgimpbase/gimpbase.h"
27 #include "libgimpwidgets/gimpwidgets.h"
28
29 #include "widgets-types.h"
30
31 #include "core/gimpchannel.h"
32 #include "core/gimpimage.h"
33
34 #include "gimpcellrendererviewable.h"
35 #include "gimpcomponenteditor.h"
36 #include "gimpdnd.h"
37 #include "gimpdocked.h"
38 #include "gimpmenufactory.h"
39 #include "gimpviewrendererimage.h"
40 #include "gimpwidgets-utils.h"
41
42 #include "gimp-intl.h"
43
44
45 enum
46 {
47 COLUMN_CHANNEL,
48 COLUMN_VISIBLE,
49 COLUMN_RENDERER,
50 COLUMN_NAME,
51 N_COLUMNS
52 };
53
54
55 static void gimp_component_editor_docked_iface_init (GimpDockedInterface *iface);
56
57 static void gimp_component_editor_set_context (GimpDocked *docked,
58 GimpContext *context);
59
60 static void gimp_component_editor_set_image (GimpImageEditor *editor,
61 GimpImage *image);
62
63 static void gimp_component_editor_create_components (GimpComponentEditor *editor);
64 static void gimp_component_editor_clear_components (GimpComponentEditor *editor);
65 static void gimp_component_editor_clicked (GtkCellRendererToggle *cellrenderertoggle,
66 gchar *path,
67 GdkModifierType state,
68 GimpComponentEditor *editor);
69 static gboolean gimp_component_editor_select (GtkTreeSelection *selection,
70 GtkTreeModel *model,
71 GtkTreePath *path,
72 gboolean path_currently_selected,
73 gpointer data);
74 static gboolean gimp_component_editor_button_press (GtkWidget *widget,
75 GdkEventButton *bevent,
76 GimpComponentEditor *editor);
77 static void gimp_component_editor_renderer_update (GimpViewRenderer *renderer,
78 GimpComponentEditor *editor);
79 static void gimp_component_editor_mode_changed (GimpImage *image,
80 GimpComponentEditor *editor);
81 static void gimp_component_editor_alpha_changed (GimpImage *image,
82 GimpComponentEditor *editor);
83 static void gimp_component_editor_visibility_changed(GimpImage *image,
84 GimpChannelType channel,
85 GimpComponentEditor *editor);
86 static void gimp_component_editor_active_changed (GimpImage *image,
87 GimpChannelType channel,
88 GimpComponentEditor *editor);
89 static GimpImage * gimp_component_editor_drag_component (GtkWidget *widget,
90 GimpContext **context,
91 GimpChannelType *channel,
92 gpointer data);
93
94
95 G_DEFINE_TYPE_WITH_CODE (GimpComponentEditor, gimp_component_editor,
96 GIMP_TYPE_IMAGE_EDITOR,
97 G_IMPLEMENT_INTERFACE (GIMP_TYPE_DOCKED,
98 gimp_component_editor_docked_iface_init))
99
100 #define parent_class gimp_component_editor_parent_class
101
102 static GimpDockedInterface *parent_docked_iface = NULL;
103
104
105 static void
gimp_component_editor_class_init(GimpComponentEditorClass * klass)106 gimp_component_editor_class_init (GimpComponentEditorClass *klass)
107 {
108 GimpImageEditorClass *image_editor_class = GIMP_IMAGE_EDITOR_CLASS (klass);
109
110 image_editor_class->set_image = gimp_component_editor_set_image;
111 }
112
113 static void
gimp_component_editor_init(GimpComponentEditor * editor)114 gimp_component_editor_init (GimpComponentEditor *editor)
115 {
116 GtkWidget *frame;
117 GtkListStore *list;
118
119 frame = gtk_frame_new (NULL);
120 gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
121 gtk_box_pack_start (GTK_BOX (editor), frame, FALSE, FALSE, 0);
122 gtk_widget_show (frame);
123
124 list = gtk_list_store_new (N_COLUMNS,
125 G_TYPE_INT,
126 G_TYPE_BOOLEAN,
127 GIMP_TYPE_VIEW_RENDERER,
128 G_TYPE_STRING);
129 editor->model = GTK_TREE_MODEL (list);
130
131 editor->view = GTK_TREE_VIEW (gtk_tree_view_new_with_model (editor->model));
132 g_object_unref (list);
133
134 gtk_tree_view_set_headers_visible (editor->view, FALSE);
135
136 editor->eye_column = gtk_tree_view_column_new ();
137 gtk_tree_view_append_column (editor->view, editor->eye_column);
138
139 editor->eye_cell = gimp_cell_renderer_toggle_new (GIMP_ICON_VISIBLE);
140 gtk_tree_view_column_pack_start (editor->eye_column, editor->eye_cell,
141 FALSE);
142 gtk_tree_view_column_set_attributes (editor->eye_column, editor->eye_cell,
143 "active", COLUMN_VISIBLE,
144 NULL);
145
146 g_signal_connect (editor->eye_cell, "clicked",
147 G_CALLBACK (gimp_component_editor_clicked),
148 editor);
149
150 editor->renderer_cell = gimp_cell_renderer_viewable_new ();
151 gtk_tree_view_insert_column_with_attributes (editor->view,
152 -1, NULL,
153 editor->renderer_cell,
154 "renderer", COLUMN_RENDERER,
155 NULL);
156
157 gtk_tree_view_insert_column_with_attributes (editor->view,
158 -1, NULL,
159 gtk_cell_renderer_text_new (),
160 "text", COLUMN_NAME,
161 NULL);
162
163 gtk_container_add (GTK_CONTAINER (frame), GTK_WIDGET (editor->view));
164 gtk_widget_show (GTK_WIDGET (editor->view));
165
166 g_signal_connect (editor->view, "button-press-event",
167 G_CALLBACK (gimp_component_editor_button_press),
168 editor);
169
170 editor->selection = gtk_tree_view_get_selection (editor->view);
171 gtk_tree_selection_set_mode (editor->selection, GTK_SELECTION_MULTIPLE);
172
173 gtk_tree_selection_set_select_function (editor->selection,
174 gimp_component_editor_select,
175 editor, NULL);
176
177 gimp_dnd_component_source_add (GTK_WIDGET (editor->view),
178 gimp_component_editor_drag_component,
179 editor);
180 }
181
182 static void
gimp_component_editor_docked_iface_init(GimpDockedInterface * iface)183 gimp_component_editor_docked_iface_init (GimpDockedInterface *iface)
184 {
185 parent_docked_iface = g_type_interface_peek_parent (iface);
186
187 if (! parent_docked_iface)
188 parent_docked_iface = g_type_default_interface_peek (GIMP_TYPE_DOCKED);
189
190 iface->set_context = gimp_component_editor_set_context;
191 }
192
193 static void
gimp_component_editor_set_context(GimpDocked * docked,GimpContext * context)194 gimp_component_editor_set_context (GimpDocked *docked,
195 GimpContext *context)
196 {
197 GimpComponentEditor *editor = GIMP_COMPONENT_EDITOR (docked);
198 GtkTreeIter iter;
199 gboolean iter_valid;
200
201 parent_docked_iface->set_context (docked, context);
202
203 for (iter_valid = gtk_tree_model_get_iter_first (editor->model, &iter);
204 iter_valid;
205 iter_valid = gtk_tree_model_iter_next (editor->model, &iter))
206 {
207 GimpViewRenderer *renderer;
208
209 gtk_tree_model_get (editor->model, &iter,
210 COLUMN_RENDERER, &renderer,
211 -1);
212
213 gimp_view_renderer_set_context (renderer, context);
214 g_object_unref (renderer);
215 }
216 }
217
218 static void
gimp_component_editor_set_image(GimpImageEditor * editor,GimpImage * image)219 gimp_component_editor_set_image (GimpImageEditor *editor,
220 GimpImage *image)
221 {
222 GimpComponentEditor *component_editor = GIMP_COMPONENT_EDITOR (editor);
223
224 if (editor->image)
225 {
226 gimp_component_editor_clear_components (component_editor);
227
228 g_signal_handlers_disconnect_by_func (editor->image,
229 gimp_component_editor_mode_changed,
230 component_editor);
231 g_signal_handlers_disconnect_by_func (editor->image,
232 gimp_component_editor_alpha_changed,
233 component_editor);
234 g_signal_handlers_disconnect_by_func (editor->image,
235 gimp_component_editor_visibility_changed,
236 component_editor);
237 g_signal_handlers_disconnect_by_func (editor->image,
238 gimp_component_editor_active_changed,
239 component_editor);
240 }
241
242 GIMP_IMAGE_EDITOR_CLASS (parent_class)->set_image (editor, image);
243
244 if (editor->image)
245 {
246 gimp_component_editor_create_components (component_editor);
247
248 g_signal_connect (editor->image, "mode-changed",
249 G_CALLBACK (gimp_component_editor_mode_changed),
250 component_editor);
251 g_signal_connect (editor->image, "alpha-changed",
252 G_CALLBACK (gimp_component_editor_alpha_changed),
253 component_editor);
254 g_signal_connect (editor->image, "component-visibility-changed",
255 G_CALLBACK (gimp_component_editor_visibility_changed),
256 component_editor);
257 g_signal_connect (editor->image, "component-active-changed",
258 G_CALLBACK (gimp_component_editor_active_changed),
259 component_editor);
260 }
261 }
262
263 GtkWidget *
gimp_component_editor_new(gint view_size,GimpMenuFactory * menu_factory)264 gimp_component_editor_new (gint view_size,
265 GimpMenuFactory *menu_factory)
266 {
267 GimpComponentEditor *editor;
268
269 g_return_val_if_fail (view_size > 0 &&
270 view_size <= GIMP_VIEWABLE_MAX_PREVIEW_SIZE, NULL);
271 g_return_val_if_fail (GIMP_IS_MENU_FACTORY (menu_factory), NULL);
272
273 editor = g_object_new (GIMP_TYPE_COMPONENT_EDITOR,
274 "menu-factory", menu_factory,
275 "menu-identifier", "<Channels>",
276 "ui-path", "/channels-popup",
277 NULL);
278
279 gimp_component_editor_set_view_size (editor, view_size);
280
281 return GTK_WIDGET (editor);
282 }
283
284 void
gimp_component_editor_set_view_size(GimpComponentEditor * editor,gint view_size)285 gimp_component_editor_set_view_size (GimpComponentEditor *editor,
286 gint view_size)
287 {
288 GtkWidget *tree_widget;
289 GtkStyle *tree_style;
290 GtkIconSize icon_size;
291 GtkTreeIter iter;
292 gboolean iter_valid;
293
294 g_return_if_fail (GIMP_IS_COMPONENT_EDITOR (editor));
295 g_return_if_fail (view_size > 0 &&
296 view_size <= GIMP_VIEWABLE_MAX_PREVIEW_SIZE);
297
298 tree_widget = GTK_WIDGET (editor->view);
299 tree_style = gtk_widget_get_style (tree_widget);
300
301 icon_size = gimp_get_icon_size (tree_widget,
302 GIMP_ICON_VISIBLE,
303 GTK_ICON_SIZE_BUTTON,
304 view_size -
305 2 * tree_style->xthickness,
306 view_size -
307 2 * tree_style->ythickness);
308
309 g_object_set (editor->eye_cell,
310 "stock-size", icon_size,
311 NULL);
312
313 for (iter_valid = gtk_tree_model_get_iter_first (editor->model, &iter);
314 iter_valid;
315 iter_valid = gtk_tree_model_iter_next (editor->model, &iter))
316 {
317 GimpViewRenderer *renderer;
318
319 gtk_tree_model_get (editor->model, &iter,
320 COLUMN_RENDERER, &renderer,
321 -1);
322
323 gimp_view_renderer_set_size (renderer, view_size, 1);
324 g_object_unref (renderer);
325 }
326
327 editor->view_size = view_size;
328
329 gtk_tree_view_columns_autosize (editor->view);
330 }
331
332 static void
gimp_component_editor_create_components(GimpComponentEditor * editor)333 gimp_component_editor_create_components (GimpComponentEditor *editor)
334 {
335 GimpImage *image = GIMP_IMAGE_EDITOR (editor)->image;
336 gint n_components = 0;
337 GimpChannelType components[MAX_CHANNELS];
338 GEnumClass *enum_class;
339 gint i;
340
341 switch (gimp_image_get_base_type (image))
342 {
343 case GIMP_RGB:
344 n_components = 3;
345 components[0] = GIMP_CHANNEL_RED;
346 components[1] = GIMP_CHANNEL_GREEN;
347 components[2] = GIMP_CHANNEL_BLUE;
348 break;
349
350 case GIMP_GRAY:
351 n_components = 1;
352 components[0] = GIMP_CHANNEL_GRAY;
353 break;
354
355 case GIMP_INDEXED:
356 n_components = 1;
357 components[0] = GIMP_CHANNEL_INDEXED;
358 break;
359 }
360
361 if (gimp_image_has_alpha (image))
362 components[n_components++] = GIMP_CHANNEL_ALPHA;
363
364 enum_class = g_type_class_ref (GIMP_TYPE_CHANNEL_TYPE);
365
366 for (i = 0; i < n_components; i++)
367 {
368 GimpViewRenderer *renderer;
369 GtkTreeIter iter;
370 GEnumValue *enum_value;
371 const gchar *desc;
372 gboolean visible;
373
374 visible = gimp_image_get_component_visible (image, components[i]);
375
376 renderer = gimp_view_renderer_new (GIMP_IMAGE_EDITOR (editor)->context,
377 G_TYPE_FROM_INSTANCE (image),
378 editor->view_size, 1, FALSE);
379 gimp_view_renderer_set_viewable (renderer, GIMP_VIEWABLE (image));
380 gimp_view_renderer_remove_idle (renderer);
381
382 GIMP_VIEW_RENDERER_IMAGE (renderer)->channel = components[i];
383
384 g_signal_connect (renderer, "update",
385 G_CALLBACK (gimp_component_editor_renderer_update),
386 editor);
387
388 enum_value = g_enum_get_value (enum_class, components[i]);
389 desc = gimp_enum_value_get_desc (enum_class, enum_value);
390
391 gtk_list_store_append (GTK_LIST_STORE (editor->model), &iter);
392
393 gtk_list_store_set (GTK_LIST_STORE (editor->model), &iter,
394 COLUMN_CHANNEL, components[i],
395 COLUMN_VISIBLE, visible,
396 COLUMN_RENDERER, renderer,
397 COLUMN_NAME, desc,
398 -1);
399
400 g_object_unref (renderer);
401
402 if (gimp_image_get_component_active (image, components[i]))
403 gtk_tree_selection_select_iter (editor->selection, &iter);
404 }
405
406 g_type_class_unref (enum_class);
407 }
408
409 static void
gimp_component_editor_clear_components(GimpComponentEditor * editor)410 gimp_component_editor_clear_components (GimpComponentEditor *editor)
411 {
412 gtk_list_store_clear (GTK_LIST_STORE (editor->model));
413
414 /* Clear the renderer so that it don't reference the viewable.
415 * See bug #149906.
416 */
417 g_object_set (editor->renderer_cell, "renderer", NULL, NULL);
418 }
419
420 static void
gimp_component_editor_clicked(GtkCellRendererToggle * cellrenderertoggle,gchar * path_str,GdkModifierType state,GimpComponentEditor * editor)421 gimp_component_editor_clicked (GtkCellRendererToggle *cellrenderertoggle,
422 gchar *path_str,
423 GdkModifierType state,
424 GimpComponentEditor *editor)
425 {
426 GtkTreePath *path;
427 GtkTreeIter iter;
428
429 path = gtk_tree_path_new_from_string (path_str);
430
431 if (gtk_tree_model_get_iter (editor->model, &iter, path))
432 {
433 GimpImage *image = GIMP_IMAGE_EDITOR (editor)->image;
434 GimpChannelType channel;
435 gboolean active;
436
437 gtk_tree_model_get (editor->model, &iter,
438 COLUMN_CHANNEL, &channel,
439 -1);
440 g_object_get (cellrenderertoggle,
441 "active", &active,
442 NULL);
443
444 gimp_image_set_component_visible (image, channel, !active);
445 gimp_image_flush (image);
446 }
447
448 gtk_tree_path_free (path);
449 }
450
451 static gboolean
gimp_component_editor_select(GtkTreeSelection * selection,GtkTreeModel * model,GtkTreePath * path,gboolean path_currently_selected,gpointer data)452 gimp_component_editor_select (GtkTreeSelection *selection,
453 GtkTreeModel *model,
454 GtkTreePath *path,
455 gboolean path_currently_selected,
456 gpointer data)
457 {
458 GimpComponentEditor *editor = GIMP_COMPONENT_EDITOR (data);
459 GtkTreeIter iter;
460 GimpChannelType channel;
461 gboolean active;
462
463 gtk_tree_model_get_iter (editor->model, &iter, path);
464 gtk_tree_model_get (editor->model, &iter,
465 COLUMN_CHANNEL, &channel,
466 -1);
467
468 active = gimp_image_get_component_active (GIMP_IMAGE_EDITOR (editor)->image,
469 channel);
470
471 return active != path_currently_selected;
472 }
473
474 static gboolean
gimp_component_editor_button_press(GtkWidget * widget,GdkEventButton * bevent,GimpComponentEditor * editor)475 gimp_component_editor_button_press (GtkWidget *widget,
476 GdkEventButton *bevent,
477 GimpComponentEditor *editor)
478 {
479 GtkTreeViewColumn *column;
480 GtkTreePath *path;
481
482 editor->clicked_component = -1;
483
484 if (gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (widget),
485 bevent->x,
486 bevent->y,
487 &path, &column, NULL, NULL))
488 {
489 GtkTreeIter iter;
490 GimpChannelType channel;
491 gboolean active;
492
493 active = gtk_tree_selection_path_is_selected (editor->selection, path);
494
495 gtk_tree_model_get_iter (editor->model, &iter, path);
496
497 gtk_tree_path_free (path);
498
499 gtk_tree_model_get (editor->model, &iter,
500 COLUMN_CHANNEL, &channel,
501 -1);
502
503 editor->clicked_component = channel;
504
505 if (gdk_event_triggers_context_menu ((GdkEvent *) bevent))
506 {
507 gimp_editor_popup_menu (GIMP_EDITOR (editor), NULL, NULL);
508 }
509 else if (bevent->type == GDK_BUTTON_PRESS && bevent->button == 1 &&
510 column != editor->eye_column)
511 {
512 GimpImage *image = GIMP_IMAGE_EDITOR (editor)->image;
513
514 gimp_image_set_component_active (image, channel, ! active);
515 gimp_image_flush (image);
516 }
517 }
518
519 return FALSE;
520 }
521
522 static gboolean
gimp_component_editor_get_iter(GimpComponentEditor * editor,GimpChannelType channel,GtkTreeIter * iter)523 gimp_component_editor_get_iter (GimpComponentEditor *editor,
524 GimpChannelType channel,
525 GtkTreeIter *iter)
526 {
527 gint index;
528
529 index = gimp_image_get_component_index (GIMP_IMAGE_EDITOR (editor)->image,
530 channel);
531
532 if (index != -1)
533 return gtk_tree_model_iter_nth_child (editor->model, iter, NULL, index);
534
535 return FALSE;
536 }
537
538 static void
gimp_component_editor_renderer_update(GimpViewRenderer * renderer,GimpComponentEditor * editor)539 gimp_component_editor_renderer_update (GimpViewRenderer *renderer,
540 GimpComponentEditor *editor)
541 {
542 GimpChannelType channel = GIMP_VIEW_RENDERER_IMAGE (renderer)->channel;
543 GtkTreeIter iter;
544
545 if (gimp_component_editor_get_iter (editor, channel, &iter))
546 {
547 GtkTreePath *path;
548
549 path = gtk_tree_model_get_path (editor->model, &iter);
550 gtk_tree_model_row_changed (editor->model, path, &iter);
551 gtk_tree_path_free (path);
552 }
553 }
554
555 static void
gimp_component_editor_mode_changed(GimpImage * image,GimpComponentEditor * editor)556 gimp_component_editor_mode_changed (GimpImage *image,
557 GimpComponentEditor *editor)
558 {
559 gimp_component_editor_clear_components (editor);
560 gimp_component_editor_create_components (editor);
561 }
562
563 static void
gimp_component_editor_alpha_changed(GimpImage * image,GimpComponentEditor * editor)564 gimp_component_editor_alpha_changed (GimpImage *image,
565 GimpComponentEditor *editor)
566 {
567 gimp_component_editor_clear_components (editor);
568 gimp_component_editor_create_components (editor);
569 }
570
571 static void
gimp_component_editor_visibility_changed(GimpImage * image,GimpChannelType channel,GimpComponentEditor * editor)572 gimp_component_editor_visibility_changed (GimpImage *image,
573 GimpChannelType channel,
574 GimpComponentEditor *editor)
575 {
576 GtkTreeIter iter;
577
578 if (gimp_component_editor_get_iter (editor, channel, &iter))
579 {
580 gboolean visible = gimp_image_get_component_visible (image, channel);
581
582 gtk_list_store_set (GTK_LIST_STORE (editor->model), &iter,
583 COLUMN_VISIBLE, visible,
584 -1);
585 }
586 }
587
588 static void
gimp_component_editor_active_changed(GimpImage * image,GimpChannelType channel,GimpComponentEditor * editor)589 gimp_component_editor_active_changed (GimpImage *image,
590 GimpChannelType channel,
591 GimpComponentEditor *editor)
592 {
593 GtkTreeIter iter;
594
595 if (gimp_component_editor_get_iter (editor, channel, &iter))
596 {
597 gboolean active = gimp_image_get_component_active (image, channel);
598
599 if (gtk_tree_selection_iter_is_selected (editor->selection, &iter) !=
600 active)
601 {
602 if (active)
603 gtk_tree_selection_select_iter (editor->selection, &iter);
604 else
605 gtk_tree_selection_unselect_iter (editor->selection, &iter);
606 }
607 }
608 }
609
610 static GimpImage *
gimp_component_editor_drag_component(GtkWidget * widget,GimpContext ** context,GimpChannelType * channel,gpointer data)611 gimp_component_editor_drag_component (GtkWidget *widget,
612 GimpContext **context,
613 GimpChannelType *channel,
614 gpointer data)
615 {
616 GimpComponentEditor *editor = GIMP_COMPONENT_EDITOR (data);
617
618 if (GIMP_IMAGE_EDITOR (editor)->image &&
619 editor->clicked_component != -1)
620 {
621 if (channel)
622 *channel = editor->clicked_component;
623
624 if (context)
625 *context = GIMP_IMAGE_EDITOR (editor)->context;
626
627 return GIMP_IMAGE_EDITOR (editor)->image;
628 }
629
630 return NULL;
631 }
632