PIKApp/app/widgets/pikadrawabletreeview.c

409 lines
16 KiB
C

/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* pikadrawabletreeview.c
* Copyright (C) 2001-2009 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 "libpikawidgets/pikawidgets.h"
#include "widgets-types.h"
#include "core/pika.h"
#include "core/pikacontext.h"
#include "core/pikadrawable.h"
#include "core/pikadrawable-edit.h"
#include "core/pikafilloptions.h"
#include "core/pikaimage.h"
#include "core/pikaimage-undo.h"
#include "core/pikapattern.h"
#include "pikacontainerview.h"
#include "pikadnd.h"
#include "pikadrawabletreeview.h"
#include "pika-intl.h"
static void pika_drawable_tree_view_view_iface_init (PikaContainerViewInterface *iface);
static void pika_drawable_tree_view_constructed (GObject *object);
static gboolean pika_drawable_tree_view_select_items (PikaContainerView *view,
GList *items,
GList *paths);
static gboolean pika_drawable_tree_view_drop_possible(PikaContainerTreeView *view,
PikaDndType src_type,
GList *src_viewables,
PikaViewable *dest_viewable,
GtkTreePath *drop_path,
GtkTreeViewDropPosition drop_pos,
GtkTreeViewDropPosition *return_drop_pos,
GdkDragAction *return_drag_action);
static void pika_drawable_tree_view_drop_viewables (PikaContainerTreeView *view,
GList *src_viewables,
PikaViewable *dest_viewable,
GtkTreeViewDropPosition drop_pos);
static void pika_drawable_tree_view_drop_color (PikaContainerTreeView *view,
const PikaRGB *color,
PikaViewable *dest_viewable,
GtkTreeViewDropPosition drop_pos);
static void pika_drawable_tree_view_set_image (PikaItemTreeView *view,
PikaImage *image);
static void pika_drawable_tree_view_floating_selection_changed
(PikaImage *image,
PikaDrawableTreeView *view);
static void pika_drawable_tree_view_new_pattern_dropped
(GtkWidget *widget,
gint x,
gint y,
PikaViewable *viewable,
gpointer data);
static void pika_drawable_tree_view_new_color_dropped
(GtkWidget *widget,
gint x,
gint y,
const PikaRGB *color,
gpointer data);
G_DEFINE_TYPE_WITH_CODE (PikaDrawableTreeView, pika_drawable_tree_view,
PIKA_TYPE_ITEM_TREE_VIEW,
G_IMPLEMENT_INTERFACE (PIKA_TYPE_CONTAINER_VIEW,
pika_drawable_tree_view_view_iface_init))
#define parent_class pika_drawable_tree_view_parent_class
static PikaContainerViewInterface *parent_view_iface = NULL;
static void
pika_drawable_tree_view_class_init (PikaDrawableTreeViewClass *klass)
{
GObjectClass *object_class;
PikaContainerTreeViewClass *tree_view_class;
PikaItemTreeViewClass *item_view_class;
object_class = G_OBJECT_CLASS (klass);
tree_view_class = PIKA_CONTAINER_TREE_VIEW_CLASS (klass);
item_view_class = PIKA_ITEM_TREE_VIEW_CLASS (klass);
object_class->constructed = pika_drawable_tree_view_constructed;
tree_view_class->drop_possible = pika_drawable_tree_view_drop_possible;
tree_view_class->drop_viewables = pika_drawable_tree_view_drop_viewables;
tree_view_class->drop_color = pika_drawable_tree_view_drop_color;
item_view_class->set_image = pika_drawable_tree_view_set_image;
item_view_class->lock_content_icon_name = PIKA_ICON_LOCK_CONTENT;
item_view_class->lock_content_tooltip = _("Lock pixels");
item_view_class->lock_position_icon_name = PIKA_ICON_LOCK_POSITION;
item_view_class->lock_position_tooltip = _("Lock position and size");
item_view_class->lock_visibility_icon_name = PIKA_ICON_LOCK_VISIBILITY;
item_view_class->lock_visibility_tooltip = _("Lock visibility");
}
static void
pika_drawable_tree_view_view_iface_init (PikaContainerViewInterface *iface)
{
parent_view_iface = g_type_interface_peek_parent (iface);
iface->select_items = pika_drawable_tree_view_select_items;
}
static void
pika_drawable_tree_view_init (PikaDrawableTreeView *view)
{
}
static void
pika_drawable_tree_view_constructed (GObject *object)
{
PikaContainerTreeView *tree_view = PIKA_CONTAINER_TREE_VIEW (object);
PikaItemTreeView *item_view = PIKA_ITEM_TREE_VIEW (object);
G_OBJECT_CLASS (parent_class)->constructed (object);
pika_dnd_viewable_dest_add (pika_item_tree_view_get_new_button (item_view),
PIKA_TYPE_PATTERN,
pika_drawable_tree_view_new_pattern_dropped,
item_view);
pika_dnd_color_dest_add (pika_item_tree_view_get_new_button (item_view),
pika_drawable_tree_view_new_color_dropped,
item_view);
pika_dnd_color_dest_add (GTK_WIDGET (tree_view->view),
NULL, tree_view);
pika_dnd_viewable_dest_add (GTK_WIDGET (tree_view->view), PIKA_TYPE_PATTERN,
NULL, tree_view);
}
/* PikaContainerView methods */
static gboolean
pika_drawable_tree_view_select_items (PikaContainerView *view,
GList *items,
GList *paths)
{
PikaItemTreeView *item_view = PIKA_ITEM_TREE_VIEW (view);
PikaImage *image = pika_item_tree_view_get_image (item_view);
gboolean success = TRUE;
if (image)
{
PikaLayer *floating_sel = pika_image_get_floating_selection (image);
success = (items == NULL ||
floating_sel == NULL ||
(g_list_length (items) == 1 &&
items->data == PIKA_VIEWABLE (floating_sel)));
if (! success)
{
Pika *pika = image->pika;
PikaContext *context = pika_get_user_context (pika);
PikaDisplay *display = pika_context_get_display (context);
pika_message_literal (pika, G_OBJECT (display), PIKA_MESSAGE_WARNING,
_("Cannot select items while a floating "
"selection is active."));
}
}
if (success)
success = parent_view_iface->select_items (view, items, paths);
return success;
}
/* PikaContainerTreeView methods */
static gboolean
pika_drawable_tree_view_drop_possible (PikaContainerTreeView *tree_view,
PikaDndType src_type,
GList *src_viewables,
PikaViewable *dest_viewable,
GtkTreePath *drop_path,
GtkTreeViewDropPosition drop_pos,
GtkTreeViewDropPosition *return_drop_pos,
GdkDragAction *return_drag_action)
{
if (PIKA_CONTAINER_TREE_VIEW_CLASS (parent_class)->drop_possible (tree_view,
src_type,
src_viewables,
dest_viewable,
drop_path,
drop_pos,
return_drop_pos,
return_drag_action))
{
if (src_type == PIKA_DND_TYPE_COLOR ||
src_type == PIKA_DND_TYPE_PATTERN)
{
if (! dest_viewable ||
pika_item_is_content_locked (PIKA_ITEM (dest_viewable), NULL) ||
pika_viewable_get_children (PIKA_VIEWABLE (dest_viewable)))
return FALSE;
if (return_drop_pos)
{
*return_drop_pos = GTK_TREE_VIEW_DROP_INTO_OR_AFTER;
}
}
return TRUE;
}
return FALSE;
}
static void
pika_drawable_tree_view_drop_viewables (PikaContainerTreeView *view,
GList *src_viewables,
PikaViewable *dest_viewable,
GtkTreeViewDropPosition drop_pos)
{
GList *iter;
for (iter = src_viewables; iter; iter = iter->next)
{
PikaViewable *src_viewable = iter->data;
if (dest_viewable && PIKA_IS_PATTERN (src_viewable))
{
PikaImage *image = pika_item_get_image (PIKA_ITEM (dest_viewable));
PikaFillOptions *options = pika_fill_options_new (image->pika, NULL, FALSE);
pika_fill_options_set_style (options, PIKA_FILL_STYLE_PATTERN);
pika_context_set_pattern (PIKA_CONTEXT (options),
PIKA_PATTERN (src_viewable));
pika_drawable_edit_fill (PIKA_DRAWABLE (dest_viewable),
options,
C_("undo-type", "Drop pattern to layer"));
g_object_unref (options);
pika_image_flush (image);
return;
}
}
PIKA_CONTAINER_TREE_VIEW_CLASS (parent_class)->drop_viewables (view,
src_viewables,
dest_viewable,
drop_pos);
}
static void
pika_drawable_tree_view_drop_color (PikaContainerTreeView *view,
const PikaRGB *color,
PikaViewable *dest_viewable,
GtkTreeViewDropPosition drop_pos)
{
if (dest_viewable)
{
PikaImage *image = pika_item_get_image (PIKA_ITEM (dest_viewable));
PikaFillOptions *options = pika_fill_options_new (image->pika, NULL, FALSE);
pika_fill_options_set_style (options, PIKA_FILL_STYLE_FG_COLOR);
pika_context_set_foreground (PIKA_CONTEXT (options), color);
pika_drawable_edit_fill (PIKA_DRAWABLE (dest_viewable),
options,
C_("undo-type", "Drop color to layer"));
g_object_unref (options);
pika_image_flush (image);
}
}
/* PikaItemTreeView methods */
static void
pika_drawable_tree_view_set_image (PikaItemTreeView *view,
PikaImage *image)
{
if (pika_item_tree_view_get_image (view))
g_signal_handlers_disconnect_by_func (pika_item_tree_view_get_image (view),
pika_drawable_tree_view_floating_selection_changed,
view);
PIKA_ITEM_TREE_VIEW_CLASS (parent_class)->set_image (view, image);
if (pika_item_tree_view_get_image (view))
g_signal_connect (pika_item_tree_view_get_image (view),
"floating-selection-changed",
G_CALLBACK (pika_drawable_tree_view_floating_selection_changed),
view);
}
/* callbacks */
static void
pika_drawable_tree_view_floating_selection_changed (PikaImage *image,
PikaDrawableTreeView *view)
{
GList *items;
items = PIKA_ITEM_TREE_VIEW_GET_CLASS (view)->get_selected_items (image);
items = g_list_copy (items);
/* update button states */
pika_container_view_select_items (PIKA_CONTAINER_VIEW (view), items);
g_list_free (items);
}
static void
pika_drawable_tree_view_new_dropped (PikaItemTreeView *view,
PikaFillOptions *options,
const gchar *undo_desc)
{
PikaImage *image = pika_item_tree_view_get_image (view);
PikaItem *item;
pika_image_undo_group_start (image, PIKA_UNDO_GROUP_EDIT_PASTE,
_("New Layer"));
item = PIKA_ITEM_TREE_VIEW_GET_CLASS (view)->new_item (image);
if (item)
pika_drawable_edit_fill (PIKA_DRAWABLE (item), options, undo_desc);
pika_image_undo_group_end (image);
pika_image_flush (image);
}
static void
pika_drawable_tree_view_new_pattern_dropped (GtkWidget *widget,
gint x,
gint y,
PikaViewable *viewable,
gpointer data)
{
PikaItemTreeView *view = PIKA_ITEM_TREE_VIEW (data);
PikaImage *image = pika_item_tree_view_get_image (view);
PikaFillOptions *options = pika_fill_options_new (image->pika, NULL, FALSE);
pika_fill_options_set_style (options, PIKA_FILL_STYLE_PATTERN);
pika_context_set_pattern (PIKA_CONTEXT (options), PIKA_PATTERN (viewable));
pika_drawable_tree_view_new_dropped (view, options,
C_("undo-type", "Drop pattern to layer"));
g_object_unref (options);
}
static void
pika_drawable_tree_view_new_color_dropped (GtkWidget *widget,
gint x,
gint y,
const PikaRGB *color,
gpointer data)
{
PikaItemTreeView *view = PIKA_ITEM_TREE_VIEW (data);
PikaImage *image = pika_item_tree_view_get_image (view);
PikaFillOptions *options = pika_fill_options_new (image->pika, NULL, FALSE);
pika_fill_options_set_style (options, PIKA_FILL_STYLE_FG_COLOR);
pika_context_set_foreground (PIKA_CONTEXT (options), color);
pika_drawable_tree_view_new_dropped (view, options,
C_("undo-type", "Drop color to layer"));
g_object_unref (options);
}