summaryrefslogtreecommitdiffstats
path: root/app/widgets/gimpcomponenteditor.c
diff options
context:
space:
mode:
Diffstat (limited to 'app/widgets/gimpcomponenteditor.c')
-rw-r--r--app/widgets/gimpcomponenteditor.c631
1 files changed, 631 insertions, 0 deletions
diff --git a/app/widgets/gimpcomponenteditor.c b/app/widgets/gimpcomponenteditor.c
new file mode 100644
index 0000000..c16548d
--- /dev/null
+++ b/app/widgets/gimpcomponenteditor.c
@@ -0,0 +1,631 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpcomponenteditor.c
+ * Copyright (C) 2003-2005 Michael Natterer <mitch@gimp.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
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <gegl.h>
+#include <gtk/gtk.h>
+
+#include "libgimpbase/gimpbase.h"
+#include "libgimpwidgets/gimpwidgets.h"
+
+#include "widgets-types.h"
+
+#include "core/gimpchannel.h"
+#include "core/gimpimage.h"
+
+#include "gimpcellrendererviewable.h"
+#include "gimpcomponenteditor.h"
+#include "gimpdnd.h"
+#include "gimpdocked.h"
+#include "gimpmenufactory.h"
+#include "gimpviewrendererimage.h"
+#include "gimpwidgets-utils.h"
+
+#include "gimp-intl.h"
+
+
+enum
+{
+ COLUMN_CHANNEL,
+ COLUMN_VISIBLE,
+ COLUMN_RENDERER,
+ COLUMN_NAME,
+ N_COLUMNS
+};
+
+
+static void gimp_component_editor_docked_iface_init (GimpDockedInterface *iface);
+
+static void gimp_component_editor_set_context (GimpDocked *docked,
+ GimpContext *context);
+
+static void gimp_component_editor_set_image (GimpImageEditor *editor,
+ GimpImage *image);
+
+static void gimp_component_editor_create_components (GimpComponentEditor *editor);
+static void gimp_component_editor_clear_components (GimpComponentEditor *editor);
+static void gimp_component_editor_clicked (GtkCellRendererToggle *cellrenderertoggle,
+ gchar *path,
+ GdkModifierType state,
+ GimpComponentEditor *editor);
+static gboolean gimp_component_editor_select (GtkTreeSelection *selection,
+ GtkTreeModel *model,
+ GtkTreePath *path,
+ gboolean path_currently_selected,
+ gpointer data);
+static gboolean gimp_component_editor_button_press (GtkWidget *widget,
+ GdkEventButton *bevent,
+ GimpComponentEditor *editor);
+static void gimp_component_editor_renderer_update (GimpViewRenderer *renderer,
+ GimpComponentEditor *editor);
+static void gimp_component_editor_mode_changed (GimpImage *image,
+ GimpComponentEditor *editor);
+static void gimp_component_editor_alpha_changed (GimpImage *image,
+ GimpComponentEditor *editor);
+static void gimp_component_editor_visibility_changed(GimpImage *image,
+ GimpChannelType channel,
+ GimpComponentEditor *editor);
+static void gimp_component_editor_active_changed (GimpImage *image,
+ GimpChannelType channel,
+ GimpComponentEditor *editor);
+static GimpImage * gimp_component_editor_drag_component (GtkWidget *widget,
+ GimpContext **context,
+ GimpChannelType *channel,
+ gpointer data);
+
+
+G_DEFINE_TYPE_WITH_CODE (GimpComponentEditor, gimp_component_editor,
+ GIMP_TYPE_IMAGE_EDITOR,
+ G_IMPLEMENT_INTERFACE (GIMP_TYPE_DOCKED,
+ gimp_component_editor_docked_iface_init))
+
+#define parent_class gimp_component_editor_parent_class
+
+static GimpDockedInterface *parent_docked_iface = NULL;
+
+
+static void
+gimp_component_editor_class_init (GimpComponentEditorClass *klass)
+{
+ GimpImageEditorClass *image_editor_class = GIMP_IMAGE_EDITOR_CLASS (klass);
+
+ image_editor_class->set_image = gimp_component_editor_set_image;
+}
+
+static void
+gimp_component_editor_init (GimpComponentEditor *editor)
+{
+ GtkWidget *frame;
+ GtkListStore *list;
+
+ frame = gtk_frame_new (NULL);
+ gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
+ gtk_box_pack_start (GTK_BOX (editor), frame, FALSE, FALSE, 0);
+ gtk_widget_show (frame);
+
+ list = gtk_list_store_new (N_COLUMNS,
+ G_TYPE_INT,
+ G_TYPE_BOOLEAN,
+ GIMP_TYPE_VIEW_RENDERER,
+ G_TYPE_STRING);
+ editor->model = GTK_TREE_MODEL (list);
+
+ editor->view = GTK_TREE_VIEW (gtk_tree_view_new_with_model (editor->model));
+ g_object_unref (list);
+
+ gtk_tree_view_set_headers_visible (editor->view, FALSE);
+
+ editor->eye_column = gtk_tree_view_column_new ();
+ gtk_tree_view_append_column (editor->view, editor->eye_column);
+
+ editor->eye_cell = gimp_cell_renderer_toggle_new (GIMP_ICON_VISIBLE);
+ gtk_tree_view_column_pack_start (editor->eye_column, editor->eye_cell,
+ FALSE);
+ gtk_tree_view_column_set_attributes (editor->eye_column, editor->eye_cell,
+ "active", COLUMN_VISIBLE,
+ NULL);
+
+ g_signal_connect (editor->eye_cell, "clicked",
+ G_CALLBACK (gimp_component_editor_clicked),
+ editor);
+
+ editor->renderer_cell = gimp_cell_renderer_viewable_new ();
+ gtk_tree_view_insert_column_with_attributes (editor->view,
+ -1, NULL,
+ editor->renderer_cell,
+ "renderer", COLUMN_RENDERER,
+ NULL);
+
+ gtk_tree_view_insert_column_with_attributes (editor->view,
+ -1, NULL,
+ gtk_cell_renderer_text_new (),
+ "text", COLUMN_NAME,
+ NULL);
+
+ gtk_container_add (GTK_CONTAINER (frame), GTK_WIDGET (editor->view));
+ gtk_widget_show (GTK_WIDGET (editor->view));
+
+ g_signal_connect (editor->view, "button-press-event",
+ G_CALLBACK (gimp_component_editor_button_press),
+ editor);
+
+ editor->selection = gtk_tree_view_get_selection (editor->view);
+ gtk_tree_selection_set_mode (editor->selection, GTK_SELECTION_MULTIPLE);
+
+ gtk_tree_selection_set_select_function (editor->selection,
+ gimp_component_editor_select,
+ editor, NULL);
+
+ gimp_dnd_component_source_add (GTK_WIDGET (editor->view),
+ gimp_component_editor_drag_component,
+ editor);
+}
+
+static void
+gimp_component_editor_docked_iface_init (GimpDockedInterface *iface)
+{
+ parent_docked_iface = g_type_interface_peek_parent (iface);
+
+ if (! parent_docked_iface)
+ parent_docked_iface = g_type_default_interface_peek (GIMP_TYPE_DOCKED);
+
+ iface->set_context = gimp_component_editor_set_context;
+}
+
+static void
+gimp_component_editor_set_context (GimpDocked *docked,
+ GimpContext *context)
+{
+ GimpComponentEditor *editor = GIMP_COMPONENT_EDITOR (docked);
+ GtkTreeIter iter;
+ gboolean iter_valid;
+
+ parent_docked_iface->set_context (docked, context);
+
+ for (iter_valid = gtk_tree_model_get_iter_first (editor->model, &iter);
+ iter_valid;
+ iter_valid = gtk_tree_model_iter_next (editor->model, &iter))
+ {
+ GimpViewRenderer *renderer;
+
+ gtk_tree_model_get (editor->model, &iter,
+ COLUMN_RENDERER, &renderer,
+ -1);
+
+ gimp_view_renderer_set_context (renderer, context);
+ g_object_unref (renderer);
+ }
+}
+
+static void
+gimp_component_editor_set_image (GimpImageEditor *editor,
+ GimpImage *image)
+{
+ GimpComponentEditor *component_editor = GIMP_COMPONENT_EDITOR (editor);
+
+ if (editor->image)
+ {
+ gimp_component_editor_clear_components (component_editor);
+
+ g_signal_handlers_disconnect_by_func (editor->image,
+ gimp_component_editor_mode_changed,
+ component_editor);
+ g_signal_handlers_disconnect_by_func (editor->image,
+ gimp_component_editor_alpha_changed,
+ component_editor);
+ g_signal_handlers_disconnect_by_func (editor->image,
+ gimp_component_editor_visibility_changed,
+ component_editor);
+ g_signal_handlers_disconnect_by_func (editor->image,
+ gimp_component_editor_active_changed,
+ component_editor);
+ }
+
+ GIMP_IMAGE_EDITOR_CLASS (parent_class)->set_image (editor, image);
+
+ if (editor->image)
+ {
+ gimp_component_editor_create_components (component_editor);
+
+ g_signal_connect (editor->image, "mode-changed",
+ G_CALLBACK (gimp_component_editor_mode_changed),
+ component_editor);
+ g_signal_connect (editor->image, "alpha-changed",
+ G_CALLBACK (gimp_component_editor_alpha_changed),
+ component_editor);
+ g_signal_connect (editor->image, "component-visibility-changed",
+ G_CALLBACK (gimp_component_editor_visibility_changed),
+ component_editor);
+ g_signal_connect (editor->image, "component-active-changed",
+ G_CALLBACK (gimp_component_editor_active_changed),
+ component_editor);
+ }
+}
+
+GtkWidget *
+gimp_component_editor_new (gint view_size,
+ GimpMenuFactory *menu_factory)
+{
+ GimpComponentEditor *editor;
+
+ g_return_val_if_fail (view_size > 0 &&
+ view_size <= GIMP_VIEWABLE_MAX_PREVIEW_SIZE, NULL);
+ g_return_val_if_fail (GIMP_IS_MENU_FACTORY (menu_factory), NULL);
+
+ editor = g_object_new (GIMP_TYPE_COMPONENT_EDITOR,
+ "menu-factory", menu_factory,
+ "menu-identifier", "<Channels>",
+ "ui-path", "/channels-popup",
+ NULL);
+
+ gimp_component_editor_set_view_size (editor, view_size);
+
+ return GTK_WIDGET (editor);
+}
+
+void
+gimp_component_editor_set_view_size (GimpComponentEditor *editor,
+ gint view_size)
+{
+ GtkWidget *tree_widget;
+ GtkStyle *tree_style;
+ GtkIconSize icon_size;
+ GtkTreeIter iter;
+ gboolean iter_valid;
+
+ g_return_if_fail (GIMP_IS_COMPONENT_EDITOR (editor));
+ g_return_if_fail (view_size > 0 &&
+ view_size <= GIMP_VIEWABLE_MAX_PREVIEW_SIZE);
+
+ tree_widget = GTK_WIDGET (editor->view);
+ tree_style = gtk_widget_get_style (tree_widget);
+
+ icon_size = gimp_get_icon_size (tree_widget,
+ GIMP_ICON_VISIBLE,
+ GTK_ICON_SIZE_BUTTON,
+ view_size -
+ 2 * tree_style->xthickness,
+ view_size -
+ 2 * tree_style->ythickness);
+
+ g_object_set (editor->eye_cell,
+ "stock-size", icon_size,
+ NULL);
+
+ for (iter_valid = gtk_tree_model_get_iter_first (editor->model, &iter);
+ iter_valid;
+ iter_valid = gtk_tree_model_iter_next (editor->model, &iter))
+ {
+ GimpViewRenderer *renderer;
+
+ gtk_tree_model_get (editor->model, &iter,
+ COLUMN_RENDERER, &renderer,
+ -1);
+
+ gimp_view_renderer_set_size (renderer, view_size, 1);
+ g_object_unref (renderer);
+ }
+
+ editor->view_size = view_size;
+
+ gtk_tree_view_columns_autosize (editor->view);
+}
+
+static void
+gimp_component_editor_create_components (GimpComponentEditor *editor)
+{
+ GimpImage *image = GIMP_IMAGE_EDITOR (editor)->image;
+ gint n_components = 0;
+ GimpChannelType components[MAX_CHANNELS];
+ GEnumClass *enum_class;
+ gint i;
+
+ switch (gimp_image_get_base_type (image))
+ {
+ case GIMP_RGB:
+ n_components = 3;
+ components[0] = GIMP_CHANNEL_RED;
+ components[1] = GIMP_CHANNEL_GREEN;
+ components[2] = GIMP_CHANNEL_BLUE;
+ break;
+
+ case GIMP_GRAY:
+ n_components = 1;
+ components[0] = GIMP_CHANNEL_GRAY;
+ break;
+
+ case GIMP_INDEXED:
+ n_components = 1;
+ components[0] = GIMP_CHANNEL_INDEXED;
+ break;
+ }
+
+ if (gimp_image_has_alpha (image))
+ components[n_components++] = GIMP_CHANNEL_ALPHA;
+
+ enum_class = g_type_class_ref (GIMP_TYPE_CHANNEL_TYPE);
+
+ for (i = 0; i < n_components; i++)
+ {
+ GimpViewRenderer *renderer;
+ GtkTreeIter iter;
+ GEnumValue *enum_value;
+ const gchar *desc;
+ gboolean visible;
+
+ visible = gimp_image_get_component_visible (image, components[i]);
+
+ renderer = gimp_view_renderer_new (GIMP_IMAGE_EDITOR (editor)->context,
+ G_TYPE_FROM_INSTANCE (image),
+ editor->view_size, 1, FALSE);
+ gimp_view_renderer_set_viewable (renderer, GIMP_VIEWABLE (image));
+ gimp_view_renderer_remove_idle (renderer);
+
+ GIMP_VIEW_RENDERER_IMAGE (renderer)->channel = components[i];
+
+ g_signal_connect (renderer, "update",
+ G_CALLBACK (gimp_component_editor_renderer_update),
+ editor);
+
+ enum_value = g_enum_get_value (enum_class, components[i]);
+ desc = gimp_enum_value_get_desc (enum_class, enum_value);
+
+ gtk_list_store_append (GTK_LIST_STORE (editor->model), &iter);
+
+ gtk_list_store_set (GTK_LIST_STORE (editor->model), &iter,
+ COLUMN_CHANNEL, components[i],
+ COLUMN_VISIBLE, visible,
+ COLUMN_RENDERER, renderer,
+ COLUMN_NAME, desc,
+ -1);
+
+ g_object_unref (renderer);
+
+ if (gimp_image_get_component_active (image, components[i]))
+ gtk_tree_selection_select_iter (editor->selection, &iter);
+ }
+
+ g_type_class_unref (enum_class);
+}
+
+static void
+gimp_component_editor_clear_components (GimpComponentEditor *editor)
+{
+ gtk_list_store_clear (GTK_LIST_STORE (editor->model));
+
+ /* Clear the renderer so that it don't reference the viewable.
+ * See bug #149906.
+ */
+ g_object_set (editor->renderer_cell, "renderer", NULL, NULL);
+}
+
+static void
+gimp_component_editor_clicked (GtkCellRendererToggle *cellrenderertoggle,
+ gchar *path_str,
+ GdkModifierType state,
+ GimpComponentEditor *editor)
+{
+ GtkTreePath *path;
+ GtkTreeIter iter;
+
+ path = gtk_tree_path_new_from_string (path_str);
+
+ if (gtk_tree_model_get_iter (editor->model, &iter, path))
+ {
+ GimpImage *image = GIMP_IMAGE_EDITOR (editor)->image;
+ GimpChannelType channel;
+ gboolean active;
+
+ gtk_tree_model_get (editor->model, &iter,
+ COLUMN_CHANNEL, &channel,
+ -1);
+ g_object_get (cellrenderertoggle,
+ "active", &active,
+ NULL);
+
+ gimp_image_set_component_visible (image, channel, !active);
+ gimp_image_flush (image);
+ }
+
+ gtk_tree_path_free (path);
+}
+
+static gboolean
+gimp_component_editor_select (GtkTreeSelection *selection,
+ GtkTreeModel *model,
+ GtkTreePath *path,
+ gboolean path_currently_selected,
+ gpointer data)
+{
+ GimpComponentEditor *editor = GIMP_COMPONENT_EDITOR (data);
+ GtkTreeIter iter;
+ GimpChannelType channel;
+ gboolean active;
+
+ gtk_tree_model_get_iter (editor->model, &iter, path);
+ gtk_tree_model_get (editor->model, &iter,
+ COLUMN_CHANNEL, &channel,
+ -1);
+
+ active = gimp_image_get_component_active (GIMP_IMAGE_EDITOR (editor)->image,
+ channel);
+
+ return active != path_currently_selected;
+}
+
+static gboolean
+gimp_component_editor_button_press (GtkWidget *widget,
+ GdkEventButton *bevent,
+ GimpComponentEditor *editor)
+{
+ GtkTreeViewColumn *column;
+ GtkTreePath *path;
+
+ editor->clicked_component = -1;
+
+ if (gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (widget),
+ bevent->x,
+ bevent->y,
+ &path, &column, NULL, NULL))
+ {
+ GtkTreeIter iter;
+ GimpChannelType channel;
+ gboolean active;
+
+ active = gtk_tree_selection_path_is_selected (editor->selection, path);
+
+ gtk_tree_model_get_iter (editor->model, &iter, path);
+
+ gtk_tree_path_free (path);
+
+ gtk_tree_model_get (editor->model, &iter,
+ COLUMN_CHANNEL, &channel,
+ -1);
+
+ editor->clicked_component = channel;
+
+ if (gdk_event_triggers_context_menu ((GdkEvent *) bevent))
+ {
+ gimp_editor_popup_menu (GIMP_EDITOR (editor), NULL, NULL);
+ }
+ else if (bevent->type == GDK_BUTTON_PRESS && bevent->button == 1 &&
+ column != editor->eye_column)
+ {
+ GimpImage *image = GIMP_IMAGE_EDITOR (editor)->image;
+
+ gimp_image_set_component_active (image, channel, ! active);
+ gimp_image_flush (image);
+ }
+ }
+
+ return FALSE;
+}
+
+static gboolean
+gimp_component_editor_get_iter (GimpComponentEditor *editor,
+ GimpChannelType channel,
+ GtkTreeIter *iter)
+{
+ gint index;
+
+ index = gimp_image_get_component_index (GIMP_IMAGE_EDITOR (editor)->image,
+ channel);
+
+ if (index != -1)
+ return gtk_tree_model_iter_nth_child (editor->model, iter, NULL, index);
+
+ return FALSE;
+}
+
+static void
+gimp_component_editor_renderer_update (GimpViewRenderer *renderer,
+ GimpComponentEditor *editor)
+{
+ GimpChannelType channel = GIMP_VIEW_RENDERER_IMAGE (renderer)->channel;
+ GtkTreeIter iter;
+
+ if (gimp_component_editor_get_iter (editor, channel, &iter))
+ {
+ GtkTreePath *path;
+
+ path = gtk_tree_model_get_path (editor->model, &iter);
+ gtk_tree_model_row_changed (editor->model, path, &iter);
+ gtk_tree_path_free (path);
+ }
+}
+
+static void
+gimp_component_editor_mode_changed (GimpImage *image,
+ GimpComponentEditor *editor)
+{
+ gimp_component_editor_clear_components (editor);
+ gimp_component_editor_create_components (editor);
+}
+
+static void
+gimp_component_editor_alpha_changed (GimpImage *image,
+ GimpComponentEditor *editor)
+{
+ gimp_component_editor_clear_components (editor);
+ gimp_component_editor_create_components (editor);
+}
+
+static void
+gimp_component_editor_visibility_changed (GimpImage *image,
+ GimpChannelType channel,
+ GimpComponentEditor *editor)
+{
+ GtkTreeIter iter;
+
+ if (gimp_component_editor_get_iter (editor, channel, &iter))
+ {
+ gboolean visible = gimp_image_get_component_visible (image, channel);
+
+ gtk_list_store_set (GTK_LIST_STORE (editor->model), &iter,
+ COLUMN_VISIBLE, visible,
+ -1);
+ }
+}
+
+static void
+gimp_component_editor_active_changed (GimpImage *image,
+ GimpChannelType channel,
+ GimpComponentEditor *editor)
+{
+ GtkTreeIter iter;
+
+ if (gimp_component_editor_get_iter (editor, channel, &iter))
+ {
+ gboolean active = gimp_image_get_component_active (image, channel);
+
+ if (gtk_tree_selection_iter_is_selected (editor->selection, &iter) !=
+ active)
+ {
+ if (active)
+ gtk_tree_selection_select_iter (editor->selection, &iter);
+ else
+ gtk_tree_selection_unselect_iter (editor->selection, &iter);
+ }
+ }
+}
+
+static GimpImage *
+gimp_component_editor_drag_component (GtkWidget *widget,
+ GimpContext **context,
+ GimpChannelType *channel,
+ gpointer data)
+{
+ GimpComponentEditor *editor = GIMP_COMPONENT_EDITOR (data);
+
+ if (GIMP_IMAGE_EDITOR (editor)->image &&
+ editor->clicked_component != -1)
+ {
+ if (channel)
+ *channel = editor->clicked_component;
+
+ if (context)
+ *context = GIMP_IMAGE_EDITOR (editor)->context;
+
+ return GIMP_IMAGE_EDITOR (editor)->image;
+ }
+
+ return NULL;
+}