2023-09-26 00:35:21 +02:00
|
|
|
/* 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
|
|
|
|
*
|
|
|
|
* pikalayertreeview.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 <string.h>
|
|
|
|
|
|
|
|
#include <gegl.h>
|
|
|
|
#include <gtk/gtk.h>
|
|
|
|
|
|
|
|
#include "libpikabase/pikabase.h"
|
|
|
|
#include "libpikamath/pikamath.h"
|
|
|
|
#include "libpikawidgets/pikawidgets.h"
|
|
|
|
|
|
|
|
#include "widgets-types.h"
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
/* For mask features in pika_layer_tree_view_layer_clicked() */
|
|
|
|
#include "config/pikadialogconfig.h"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include "core/pika.h"
|
|
|
|
#include "core/pikachannel.h"
|
|
|
|
#include "core/pikacontainer.h"
|
|
|
|
#include "core/pikacontext.h"
|
|
|
|
#include "core/pikaimage-undo.h"
|
|
|
|
#include "core/pikaimage-undo-push.h"
|
|
|
|
#include "core/pikaimage.h"
|
|
|
|
#include "core/pikaitemlist.h"
|
|
|
|
#include "core/pikaitemundo.h"
|
|
|
|
#include "core/pikalayer.h"
|
|
|
|
#include "core/pikalayer-floating-selection.h"
|
|
|
|
#include "core/pikalayer-new.h"
|
|
|
|
#include "core/pikalayermask.h"
|
|
|
|
#include "core/pikatreehandler.h"
|
|
|
|
|
|
|
|
#include "text/pikatextlayer.h"
|
|
|
|
|
|
|
|
#include "file/file-open.h"
|
|
|
|
|
|
|
|
#include "pikaactiongroup.h"
|
|
|
|
#include "pikacellrendererviewable.h"
|
|
|
|
#include "pikacontainertreestore.h"
|
|
|
|
#include "pikacontainerview.h"
|
|
|
|
#include "pikadnd.h"
|
|
|
|
#include "pikahelp-ids.h"
|
|
|
|
#include "pikalayermodebox.h"
|
|
|
|
#include "pikalayertreeview.h"
|
|
|
|
#include "pikatoggleaction.h"
|
|
|
|
#include "pikauimanager.h"
|
|
|
|
#include "pikaviewrenderer.h"
|
|
|
|
#include "pikawidgets-utils.h"
|
|
|
|
|
|
|
|
#include "pika-intl.h"
|
|
|
|
|
|
|
|
|
|
|
|
struct _PikaLayerTreeViewPrivate
|
|
|
|
{
|
|
|
|
GtkWidget *layer_mode_box;
|
|
|
|
GtkAdjustment *opacity_adjustment;
|
|
|
|
GtkWidget *anchor_button;
|
|
|
|
|
|
|
|
GtkWidget *link_button;
|
|
|
|
GtkWidget *link_popover;
|
|
|
|
GtkWidget *new_link_button;
|
|
|
|
GtkWidget *link_list;
|
|
|
|
GtkWidget *link_entry;
|
|
|
|
GtkWidget *link_search_entry;
|
|
|
|
PikaItemList *link_pattern_set;
|
|
|
|
|
|
|
|
gint model_column_mask;
|
|
|
|
gint model_column_mask_visible;
|
|
|
|
|
|
|
|
GtkCellRenderer *mask_cell;
|
|
|
|
|
|
|
|
PangoAttrList *italic_attrs;
|
|
|
|
PangoAttrList *bold_attrs;
|
|
|
|
|
|
|
|
PikaTreeHandler *mode_changed_handler;
|
|
|
|
PikaTreeHandler *opacity_changed_handler;
|
|
|
|
PikaTreeHandler *mask_changed_handler;
|
|
|
|
PikaTreeHandler *alpha_changed_handler;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
static void pika_layer_tree_view_view_iface_init (PikaContainerViewInterface *iface);
|
|
|
|
|
|
|
|
static void pika_layer_tree_view_constructed (GObject *object);
|
|
|
|
static void pika_layer_tree_view_finalize (GObject *object);
|
|
|
|
|
|
|
|
static void pika_layer_tree_view_style_updated (GtkWidget *widget);
|
|
|
|
|
|
|
|
static void pika_layer_tree_view_set_container (PikaContainerView *view,
|
|
|
|
PikaContainer *container);
|
|
|
|
static void pika_layer_tree_view_set_context (PikaContainerView *view,
|
|
|
|
PikaContext *context);
|
|
|
|
static gpointer pika_layer_tree_view_insert_item (PikaContainerView *view,
|
|
|
|
PikaViewable *viewable,
|
|
|
|
gpointer parent_insert_data,
|
|
|
|
gint index);
|
|
|
|
static gboolean pika_layer_tree_view_select_items (PikaContainerView *view,
|
|
|
|
GList *items,
|
|
|
|
GList *paths);
|
|
|
|
static void pika_layer_tree_view_set_view_size (PikaContainerView *view);
|
|
|
|
static gboolean pika_layer_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_layer_tree_view_drop_color (PikaContainerTreeView *view,
|
|
|
|
const PikaRGB *color,
|
|
|
|
PikaViewable *dest_viewable,
|
|
|
|
GtkTreeViewDropPosition drop_pos);
|
|
|
|
static void pika_layer_tree_view_drop_uri_list (PikaContainerTreeView *view,
|
|
|
|
GList *uri_list,
|
|
|
|
PikaViewable *dest_viewable,
|
|
|
|
GtkTreeViewDropPosition drop_pos);
|
|
|
|
static void pika_layer_tree_view_drop_component (PikaContainerTreeView *tree_view,
|
|
|
|
PikaImage *image,
|
|
|
|
PikaChannelType component,
|
|
|
|
PikaViewable *dest_viewable,
|
|
|
|
GtkTreeViewDropPosition drop_pos);
|
|
|
|
static void pika_layer_tree_view_drop_pixbuf (PikaContainerTreeView *tree_view,
|
|
|
|
GdkPixbuf *pixbuf,
|
|
|
|
PikaViewable *dest_viewable,
|
|
|
|
GtkTreeViewDropPosition drop_pos);
|
|
|
|
static void pika_layer_tree_view_set_image (PikaItemTreeView *view,
|
|
|
|
PikaImage *image);
|
|
|
|
static PikaItem * pika_layer_tree_view_item_new (PikaImage *image);
|
|
|
|
static void pika_layer_tree_view_floating_selection_changed (PikaImage *image,
|
|
|
|
PikaLayerTreeView *view);
|
|
|
|
|
|
|
|
static void pika_layer_tree_view_layer_links_changed (PikaImage *image,
|
|
|
|
PikaLayerTreeView *view);
|
|
|
|
static gboolean pika_layer_tree_view_link_clicked (GtkWidget *box,
|
|
|
|
GdkEvent *event,
|
|
|
|
PikaLayerTreeView *view);
|
|
|
|
static void pika_layer_tree_view_link_popover_shown (GtkPopover *popover,
|
|
|
|
PikaLayerTreeView *view);
|
|
|
|
|
|
|
|
static gboolean pika_layer_tree_view_search_key_release (GtkWidget *widget,
|
|
|
|
GdkEventKey *event,
|
|
|
|
PikaLayerTreeView *view);
|
|
|
|
static gboolean pika_layer_tree_view_start_interactive_search (GtkTreeView *tree_view,
|
|
|
|
PikaLayerTreeView *layer_view);
|
|
|
|
|
|
|
|
static void pika_layer_tree_view_new_link_exit (PikaLayerTreeView *view);
|
|
|
|
static gboolean pika_layer_tree_view_new_link_clicked (PikaLayerTreeView *view);
|
|
|
|
static gboolean pika_layer_tree_view_unlink_clicked (GtkWidget *widget,
|
|
|
|
GdkEvent *event,
|
|
|
|
PikaLayerTreeView *view);
|
|
|
|
|
|
|
|
static void pika_layer_tree_view_layer_mode_box_callback (GtkWidget *widget,
|
|
|
|
const GParamSpec *pspec,
|
|
|
|
PikaLayerTreeView *view);
|
|
|
|
static void pika_layer_tree_view_opacity_scale_changed (GtkAdjustment *adj,
|
|
|
|
PikaLayerTreeView *view);
|
|
|
|
static void pika_layer_tree_view_layer_signal_handler (PikaLayer *layer,
|
|
|
|
PikaLayerTreeView *view);
|
|
|
|
static void pika_layer_tree_view_update_options (PikaLayerTreeView *view,
|
|
|
|
GList *layers);
|
|
|
|
static void pika_layer_tree_view_update_menu (PikaLayerTreeView *view,
|
|
|
|
GList *layers);
|
|
|
|
static void pika_layer_tree_view_update_highlight (PikaLayerTreeView *view);
|
|
|
|
static void pika_layer_tree_view_mask_update (PikaLayerTreeView *view,
|
|
|
|
GtkTreeIter *iter,
|
|
|
|
PikaLayer *layer);
|
|
|
|
static void pika_layer_tree_view_mask_changed (PikaLayer *layer,
|
|
|
|
PikaLayerTreeView *view);
|
|
|
|
static void pika_layer_tree_view_renderer_update (PikaViewRenderer *renderer,
|
|
|
|
PikaLayerTreeView *view);
|
|
|
|
static void pika_layer_tree_view_update_borders (PikaLayerTreeView *view,
|
|
|
|
GtkTreeIter *iter);
|
|
|
|
static void pika_layer_tree_view_mask_callback (PikaLayer *mask,
|
|
|
|
PikaLayerTreeView *view);
|
|
|
|
static void pika_layer_tree_view_layer_clicked (PikaCellRendererViewable *cell,
|
|
|
|
const gchar *path,
|
|
|
|
GdkModifierType state,
|
|
|
|
PikaLayerTreeView *view);
|
|
|
|
static void pika_layer_tree_view_mask_clicked (PikaCellRendererViewable *cell,
|
|
|
|
const gchar *path,
|
|
|
|
GdkModifierType state,
|
|
|
|
PikaLayerTreeView *view);
|
|
|
|
static void pika_layer_tree_view_alpha_update (PikaLayerTreeView *view,
|
|
|
|
GtkTreeIter *iter,
|
|
|
|
PikaLayer *layer);
|
|
|
|
static void pika_layer_tree_view_alpha_changed (PikaLayer *layer,
|
|
|
|
PikaLayerTreeView *view);
|
|
|
|
|
|
|
|
|
|
|
|
G_DEFINE_TYPE_WITH_CODE (PikaLayerTreeView, pika_layer_tree_view,
|
|
|
|
PIKA_TYPE_DRAWABLE_TREE_VIEW,
|
|
|
|
G_ADD_PRIVATE (PikaLayerTreeView)
|
|
|
|
G_IMPLEMENT_INTERFACE (PIKA_TYPE_CONTAINER_VIEW,
|
|
|
|
pika_layer_tree_view_view_iface_init))
|
|
|
|
|
|
|
|
#define parent_class pika_layer_tree_view_parent_class
|
|
|
|
|
|
|
|
static PikaContainerViewInterface *parent_view_iface = NULL;
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
pika_layer_tree_view_class_init (PikaLayerTreeViewClass *klass)
|
|
|
|
{
|
|
|
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
|
|
|
PikaContainerTreeViewClass *tree_view_class;
|
|
|
|
PikaItemTreeViewClass *item_view_class;
|
|
|
|
GtkWidgetClass *widget_class;
|
|
|
|
|
|
|
|
widget_class = GTK_WIDGET_CLASS (klass);
|
|
|
|
tree_view_class = PIKA_CONTAINER_TREE_VIEW_CLASS (klass);
|
|
|
|
item_view_class = PIKA_ITEM_TREE_VIEW_CLASS (klass);
|
|
|
|
|
|
|
|
object_class->constructed = pika_layer_tree_view_constructed;
|
|
|
|
object_class->finalize = pika_layer_tree_view_finalize;
|
|
|
|
|
|
|
|
widget_class->style_updated = pika_layer_tree_view_style_updated;
|
|
|
|
|
|
|
|
tree_view_class->drop_possible = pika_layer_tree_view_drop_possible;
|
|
|
|
tree_view_class->drop_color = pika_layer_tree_view_drop_color;
|
|
|
|
tree_view_class->drop_uri_list = pika_layer_tree_view_drop_uri_list;
|
|
|
|
tree_view_class->drop_component = pika_layer_tree_view_drop_component;
|
|
|
|
tree_view_class->drop_pixbuf = pika_layer_tree_view_drop_pixbuf;
|
|
|
|
|
|
|
|
item_view_class->item_type = PIKA_TYPE_LAYER;
|
|
|
|
item_view_class->signal_name = "selected-layers-changed";
|
|
|
|
|
|
|
|
item_view_class->set_image = pika_layer_tree_view_set_image;
|
|
|
|
item_view_class->get_container = pika_image_get_layers;
|
|
|
|
item_view_class->get_selected_items = (PikaGetItemsFunc) pika_image_get_selected_layers;
|
|
|
|
item_view_class->set_selected_items = (PikaSetItemsFunc) pika_image_set_selected_layers;
|
|
|
|
item_view_class->add_item = (PikaAddItemFunc) pika_image_add_layer;
|
|
|
|
item_view_class->remove_item = (PikaRemoveItemFunc) pika_image_remove_layer;
|
|
|
|
item_view_class->new_item = pika_layer_tree_view_item_new;
|
|
|
|
|
|
|
|
item_view_class->action_group = "layers";
|
|
|
|
item_view_class->activate_action = "layers-edit";
|
|
|
|
item_view_class->new_action = "layers-new";
|
|
|
|
item_view_class->new_default_action = "layers-new-last-values";
|
|
|
|
item_view_class->raise_action = "layers-raise";
|
|
|
|
item_view_class->raise_top_action = "layers-raise-to-top";
|
|
|
|
item_view_class->lower_action = "layers-lower";
|
|
|
|
item_view_class->lower_bottom_action = "layers-lower-to-bottom";
|
|
|
|
item_view_class->duplicate_action = "layers-duplicate";
|
|
|
|
item_view_class->delete_action = "layers-delete";
|
|
|
|
item_view_class->lock_content_help_id = PIKA_HELP_LAYER_LOCK_PIXELS;
|
|
|
|
item_view_class->lock_position_help_id = PIKA_HELP_LAYER_LOCK_POSITION;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
pika_layer_tree_view_view_iface_init (PikaContainerViewInterface *iface)
|
|
|
|
{
|
|
|
|
parent_view_iface = g_type_interface_peek_parent (iface);
|
|
|
|
|
|
|
|
iface->set_container = pika_layer_tree_view_set_container;
|
|
|
|
iface->set_context = pika_layer_tree_view_set_context;
|
|
|
|
iface->insert_item = pika_layer_tree_view_insert_item;
|
|
|
|
iface->select_items = pika_layer_tree_view_select_items;
|
|
|
|
iface->set_view_size = pika_layer_tree_view_set_view_size;
|
|
|
|
|
|
|
|
iface->model_is_tree = TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
pika_layer_tree_view_init (PikaLayerTreeView *view)
|
|
|
|
{
|
|
|
|
PikaContainerTreeView *tree_view = PIKA_CONTAINER_TREE_VIEW (view);
|
|
|
|
GtkWidget *scale;
|
|
|
|
PangoAttribute *attr;
|
|
|
|
|
|
|
|
view->priv = pika_layer_tree_view_get_instance_private (view);
|
|
|
|
|
|
|
|
view->priv->link_pattern_set = NULL;
|
|
|
|
|
|
|
|
view->priv->model_column_mask =
|
|
|
|
pika_container_tree_store_columns_add (tree_view->model_columns,
|
|
|
|
&tree_view->n_model_columns,
|
|
|
|
PIKA_TYPE_VIEW_RENDERER);
|
|
|
|
|
|
|
|
view->priv->model_column_mask_visible =
|
|
|
|
pika_container_tree_store_columns_add (tree_view->model_columns,
|
|
|
|
&tree_view->n_model_columns,
|
|
|
|
G_TYPE_BOOLEAN);
|
|
|
|
|
|
|
|
/* Paint mode menu */
|
|
|
|
|
|
|
|
view->priv->layer_mode_box = pika_layer_mode_box_new (PIKA_LAYER_MODE_CONTEXT_LAYER);
|
|
|
|
pika_layer_mode_box_set_label (PIKA_LAYER_MODE_BOX (view->priv->layer_mode_box),
|
|
|
|
_("Mode"));
|
|
|
|
pika_item_tree_view_add_options (PIKA_ITEM_TREE_VIEW (view), NULL,
|
|
|
|
view->priv->layer_mode_box);
|
|
|
|
|
|
|
|
g_signal_connect (view->priv->layer_mode_box, "notify::layer-mode",
|
|
|
|
G_CALLBACK (pika_layer_tree_view_layer_mode_box_callback),
|
|
|
|
view);
|
|
|
|
|
|
|
|
pika_help_set_help_data (view->priv->layer_mode_box, NULL,
|
|
|
|
PIKA_HELP_LAYER_DIALOG_PAINT_MODE_MENU);
|
|
|
|
|
|
|
|
/* Opacity scale */
|
|
|
|
|
|
|
|
view->priv->opacity_adjustment = gtk_adjustment_new (100.0, 0.0, 100.0,
|
|
|
|
1.0, 10.0, 0.0);
|
|
|
|
scale = pika_spin_scale_new (view->priv->opacity_adjustment, _("Opacity"), 1);
|
|
|
|
pika_spin_scale_set_constrain_drag (PIKA_SPIN_SCALE (scale), TRUE);
|
|
|
|
pika_help_set_help_data (scale, NULL,
|
|
|
|
PIKA_HELP_LAYER_DIALOG_OPACITY_SCALE);
|
|
|
|
pika_item_tree_view_add_options (PIKA_ITEM_TREE_VIEW (view),
|
|
|
|
NULL, scale);
|
|
|
|
|
|
|
|
g_signal_connect (view->priv->opacity_adjustment, "value-changed",
|
|
|
|
G_CALLBACK (pika_layer_tree_view_opacity_scale_changed),
|
|
|
|
view);
|
|
|
|
|
|
|
|
view->priv->italic_attrs = pango_attr_list_new ();
|
|
|
|
attr = pango_attr_style_new (PANGO_STYLE_ITALIC);
|
|
|
|
attr->start_index = 0;
|
|
|
|
attr->end_index = -1;
|
|
|
|
pango_attr_list_insert (view->priv->italic_attrs, attr);
|
|
|
|
|
|
|
|
view->priv->bold_attrs = pango_attr_list_new ();
|
|
|
|
attr = pango_attr_weight_new (PANGO_WEIGHT_BOLD);
|
|
|
|
attr->start_index = 0;
|
|
|
|
attr->end_index = -1;
|
|
|
|
pango_attr_list_insert (view->priv->bold_attrs, attr);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
pika_layer_tree_view_constructed (GObject *object)
|
|
|
|
{
|
|
|
|
PikaContainerTreeView *tree_view = PIKA_CONTAINER_TREE_VIEW (object);
|
|
|
|
PikaLayerTreeView *layer_view = PIKA_LAYER_TREE_VIEW (object);
|
|
|
|
GtkWidget *button;
|
|
|
|
GtkWidget *placeholder;
|
|
|
|
GtkWidget *grid;
|
|
|
|
PangoAttrList *attrs;
|
|
|
|
GtkIconSize button_size;
|
|
|
|
|
|
|
|
G_OBJECT_CLASS (parent_class)->constructed (object);
|
|
|
|
|
|
|
|
layer_view->priv->mask_cell = pika_cell_renderer_viewable_new ();
|
|
|
|
gtk_tree_view_column_pack_start (tree_view->main_column,
|
|
|
|
layer_view->priv->mask_cell,
|
|
|
|
FALSE);
|
|
|
|
gtk_tree_view_column_set_attributes (tree_view->main_column,
|
|
|
|
layer_view->priv->mask_cell,
|
|
|
|
"renderer",
|
|
|
|
layer_view->priv->model_column_mask,
|
|
|
|
"visible",
|
|
|
|
layer_view->priv->model_column_mask_visible,
|
|
|
|
NULL);
|
|
|
|
|
|
|
|
pika_container_tree_view_add_renderer_cell (tree_view,
|
|
|
|
layer_view->priv->mask_cell,
|
|
|
|
layer_view->priv->model_column_mask);
|
|
|
|
|
|
|
|
g_signal_connect (tree_view->renderer_cell, "clicked",
|
|
|
|
G_CALLBACK (pika_layer_tree_view_layer_clicked),
|
|
|
|
layer_view);
|
|
|
|
g_signal_connect (layer_view->priv->mask_cell, "clicked",
|
|
|
|
G_CALLBACK (pika_layer_tree_view_mask_clicked),
|
|
|
|
layer_view);
|
|
|
|
|
|
|
|
pika_dnd_component_dest_add (GTK_WIDGET (tree_view->view),
|
|
|
|
NULL, tree_view);
|
|
|
|
pika_dnd_viewable_dest_add (GTK_WIDGET (tree_view->view), PIKA_TYPE_CHANNEL,
|
|
|
|
NULL, tree_view);
|
|
|
|
pika_dnd_viewable_dest_add (GTK_WIDGET (tree_view->view), PIKA_TYPE_LAYER_MASK,
|
|
|
|
NULL, tree_view);
|
|
|
|
pika_dnd_uri_list_dest_add (GTK_WIDGET (tree_view->view),
|
|
|
|
NULL, tree_view);
|
|
|
|
pika_dnd_pixbuf_dest_add (GTK_WIDGET (tree_view->view),
|
|
|
|
NULL, tree_view);
|
|
|
|
|
|
|
|
button = pika_editor_add_action_button (PIKA_EDITOR (layer_view), "layers",
|
|
|
|
"layers-new-group", NULL);
|
|
|
|
gtk_box_reorder_child (pika_editor_get_button_box (PIKA_EDITOR (layer_view)),
|
|
|
|
button, 1);
|
|
|
|
|
|
|
|
button = pika_editor_add_action_button (PIKA_EDITOR (layer_view), "layers",
|
|
|
|
"layers-anchor", NULL);
|
|
|
|
layer_view->priv->anchor_button = button;
|
|
|
|
pika_container_view_enable_dnd (PIKA_CONTAINER_VIEW (layer_view),
|
|
|
|
GTK_BUTTON (button),
|
|
|
|
PIKA_TYPE_LAYER);
|
|
|
|
gtk_box_reorder_child (pika_editor_get_button_box (PIKA_EDITOR (layer_view)),
|
|
|
|
button, 5);
|
|
|
|
|
|
|
|
button = pika_editor_add_action_button (PIKA_EDITOR (layer_view), "layers",
|
|
|
|
"layers-merge-down-button",
|
|
|
|
"layers-merge-group",
|
|
|
|
GDK_SHIFT_MASK,
|
|
|
|
"layers-merge-layers",
|
|
|
|
GDK_CONTROL_MASK,
|
|
|
|
"layers-merge-layers-last-values",
|
|
|
|
GDK_CONTROL_MASK |
|
|
|
|
GDK_SHIFT_MASK,
|
|
|
|
NULL);
|
|
|
|
pika_container_view_enable_dnd (PIKA_CONTAINER_VIEW (layer_view),
|
|
|
|
GTK_BUTTON (button),
|
|
|
|
PIKA_TYPE_LAYER);
|
|
|
|
gtk_box_reorder_child (pika_editor_get_button_box (PIKA_EDITOR (layer_view)),
|
|
|
|
button, 6);
|
|
|
|
|
|
|
|
button = pika_editor_add_action_button (PIKA_EDITOR (layer_view), "layers",
|
|
|
|
"layers-mask-add-button",
|
|
|
|
"layers-mask-add-last-values",
|
|
|
|
pika_get_extend_selection_mask (),
|
|
|
|
"layers-mask-delete",
|
|
|
|
pika_get_modify_selection_mask (),
|
|
|
|
"layers-mask-apply",
|
|
|
|
pika_get_extend_selection_mask () |
|
|
|
|
pika_get_modify_selection_mask (),
|
|
|
|
NULL);
|
|
|
|
pika_container_view_enable_dnd (PIKA_CONTAINER_VIEW (layer_view),
|
|
|
|
GTK_BUTTON (button),
|
|
|
|
PIKA_TYPE_LAYER);
|
|
|
|
gtk_box_reorder_child (pika_editor_get_button_box (PIKA_EDITOR (layer_view)),
|
|
|
|
button, 7);
|
|
|
|
|
|
|
|
/* Link popover menu. */
|
|
|
|
|
|
|
|
layer_view->priv->link_button = gtk_menu_button_new ();
|
|
|
|
gtk_widget_set_tooltip_text (layer_view->priv->link_button,
|
|
|
|
_("Select layers by patterns and store layer sets"));
|
|
|
|
gtk_widget_style_get (GTK_WIDGET (layer_view),
|
|
|
|
"button-icon-size", &button_size,
|
|
|
|
NULL);
|
|
|
|
gtk_button_set_image (GTK_BUTTON (layer_view->priv->link_button),
|
|
|
|
gtk_image_new_from_icon_name (PIKA_ICON_LINKED,
|
|
|
|
button_size));
|
|
|
|
gtk_box_pack_start (pika_editor_get_button_box (PIKA_EDITOR (layer_view)),
|
|
|
|
layer_view->priv->link_button, TRUE, TRUE, 0);
|
|
|
|
gtk_widget_show (layer_view->priv->link_button);
|
|
|
|
pika_container_view_enable_dnd (PIKA_CONTAINER_VIEW (layer_view),
|
|
|
|
GTK_BUTTON (layer_view->priv->link_button),
|
|
|
|
PIKA_TYPE_LAYER);
|
|
|
|
gtk_box_reorder_child (pika_editor_get_button_box (PIKA_EDITOR (layer_view)),
|
|
|
|
layer_view->priv->link_button, 8);
|
|
|
|
|
|
|
|
layer_view->priv->link_popover = gtk_popover_new (layer_view->priv->link_button);
|
|
|
|
gtk_popover_set_modal (GTK_POPOVER (layer_view->priv->link_popover), TRUE);
|
|
|
|
gtk_menu_button_set_popover (GTK_MENU_BUTTON (layer_view->priv->link_button),
|
|
|
|
layer_view->priv->link_popover);
|
|
|
|
|
|
|
|
g_signal_connect (layer_view->priv->link_popover,
|
|
|
|
"show",
|
|
|
|
G_CALLBACK (pika_layer_tree_view_link_popover_shown),
|
|
|
|
layer_view);
|
|
|
|
|
|
|
|
grid = gtk_grid_new ();
|
|
|
|
|
|
|
|
/* Link popover: regexp search. */
|
|
|
|
layer_view->priv->link_search_entry = gtk_entry_new ();
|
|
|
|
gtk_entry_set_icon_from_icon_name (GTK_ENTRY (layer_view->priv->link_search_entry),
|
|
|
|
GTK_ENTRY_ICON_SECONDARY,
|
|
|
|
"system-search");
|
|
|
|
gtk_grid_attach (GTK_GRID (grid),
|
|
|
|
layer_view->priv->link_search_entry,
|
|
|
|
0, 0, 2, 1);
|
|
|
|
gtk_widget_show (layer_view->priv->link_search_entry);
|
|
|
|
g_signal_connect (layer_view->priv->link_search_entry,
|
|
|
|
"key-release-event",
|
|
|
|
G_CALLBACK (pika_layer_tree_view_search_key_release),
|
|
|
|
layer_view);
|
|
|
|
|
|
|
|
g_signal_connect (PIKA_CONTAINER_TREE_VIEW (layer_view)->view,
|
|
|
|
"start-interactive-search",
|
|
|
|
G_CALLBACK (pika_layer_tree_view_start_interactive_search),
|
|
|
|
layer_view);
|
|
|
|
gtk_tree_view_set_search_entry (PIKA_CONTAINER_TREE_VIEW (layer_view)->view,
|
|
|
|
GTK_ENTRY (layer_view->priv->link_search_entry));
|
|
|
|
|
|
|
|
/* Link popover: existing links. */
|
|
|
|
layer_view->priv->link_list = gtk_list_box_new ();
|
|
|
|
placeholder = gtk_label_new (_("No layer set stored"));
|
|
|
|
attrs = pango_attr_list_new ();
|
|
|
|
gtk_label_set_attributes (GTK_LABEL (placeholder),
|
|
|
|
attrs);
|
|
|
|
pango_attr_list_insert (attrs, pango_attr_style_new (PANGO_STYLE_ITALIC));
|
|
|
|
pango_attr_list_insert (attrs, pango_attr_weight_new (PANGO_WEIGHT_ULTRALIGHT));
|
|
|
|
pango_attr_list_unref (attrs);
|
|
|
|
gtk_list_box_set_placeholder (GTK_LIST_BOX (layer_view->priv->link_list),
|
|
|
|
placeholder);
|
|
|
|
gtk_widget_show (placeholder);
|
|
|
|
gtk_grid_attach (GTK_GRID (grid),
|
|
|
|
layer_view->priv->link_list,
|
|
|
|
0, 1, 2, 1);
|
|
|
|
gtk_widget_show (layer_view->priv->link_list);
|
|
|
|
|
|
|
|
gtk_list_box_set_activate_on_single_click (GTK_LIST_BOX (layer_view->priv->link_list),
|
|
|
|
TRUE);
|
|
|
|
|
|
|
|
/* Link popover: new links. */
|
|
|
|
|
|
|
|
layer_view->priv->link_entry = gtk_entry_new ();
|
|
|
|
gtk_entry_set_placeholder_text (GTK_ENTRY (layer_view->priv->link_entry),
|
|
|
|
_("New layer set's name"));
|
|
|
|
gtk_grid_attach (GTK_GRID (grid),
|
|
|
|
layer_view->priv->link_entry,
|
|
|
|
0, 2, 1, 1);
|
|
|
|
gtk_widget_show (layer_view->priv->link_entry);
|
|
|
|
|
|
|
|
button = gtk_button_new_from_icon_name (PIKA_ICON_LIST_ADD, button_size);
|
|
|
|
gtk_grid_attach (GTK_GRID (grid),
|
|
|
|
button,
|
|
|
|
1, 2, 1, 1);
|
|
|
|
g_signal_connect_swapped (button,
|
|
|
|
"clicked",
|
|
|
|
G_CALLBACK (pika_layer_tree_view_new_link_clicked),
|
|
|
|
layer_view);
|
|
|
|
gtk_widget_show (button);
|
|
|
|
layer_view->priv->new_link_button = button;
|
|
|
|
|
|
|
|
/* Enter on any entry activates the link creation then exits in case
|
|
|
|
* of success.
|
|
|
|
*/
|
|
|
|
g_signal_connect_swapped (layer_view->priv->link_entry,
|
|
|
|
"activate",
|
|
|
|
G_CALLBACK (pika_layer_tree_view_new_link_exit),
|
|
|
|
layer_view);
|
|
|
|
|
|
|
|
gtk_container_add (GTK_CONTAINER (layer_view->priv->link_popover), grid);
|
|
|
|
gtk_widget_show (grid);
|
|
|
|
|
|
|
|
/* Lock alpha toggle */
|
|
|
|
|
|
|
|
pika_item_tree_view_add_lock (PIKA_ITEM_TREE_VIEW (tree_view),
|
|
|
|
PIKA_ICON_LOCK_ALPHA,
|
|
|
|
(PikaIsLockedFunc) pika_layer_get_lock_alpha,
|
|
|
|
(PikaCanLockFunc) pika_layer_can_lock_alpha,
|
|
|
|
(PikaSetLockFunc) pika_layer_set_lock_alpha,
|
|
|
|
(PikaUndoLockPush) pika_image_undo_push_layer_lock_alpha,
|
|
|
|
"lock-alpha-changed",
|
|
|
|
PIKA_UNDO_LAYER_LOCK_ALPHA,
|
|
|
|
PIKA_UNDO_GROUP_LAYER_LOCK_ALPHA,
|
|
|
|
_("Lock alpha channel"),
|
|
|
|
_("Unlock alpha channel"),
|
|
|
|
_("Set Item Exclusive Alpha Channel lock"),
|
|
|
|
_("Lock alpha channel"),
|
|
|
|
PIKA_HELP_LAYER_LOCK_ALPHA);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
pika_layer_tree_view_finalize (GObject *object)
|
|
|
|
{
|
|
|
|
PikaLayerTreeView *layer_view = PIKA_LAYER_TREE_VIEW (object);
|
|
|
|
|
|
|
|
if (layer_view->priv->italic_attrs)
|
|
|
|
{
|
|
|
|
pango_attr_list_unref (layer_view->priv->italic_attrs);
|
|
|
|
layer_view->priv->italic_attrs = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (layer_view->priv->bold_attrs)
|
|
|
|
{
|
|
|
|
pango_attr_list_unref (layer_view->priv->bold_attrs);
|
|
|
|
layer_view->priv->bold_attrs = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
G_OBJECT_CLASS (parent_class)->finalize (object);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* PikaWidget methods */
|
|
|
|
|
|
|
|
static void
|
|
|
|
pika_layer_tree_view_style_updated (GtkWidget *widget)
|
|
|
|
{
|
|
|
|
PikaLayerTreeView *view = PIKA_LAYER_TREE_VIEW (widget);
|
|
|
|
GtkWidget *image;
|
|
|
|
const gchar *old_icon_name;
|
|
|
|
GtkReliefStyle button_relief;
|
|
|
|
GtkIconSize old_size;
|
|
|
|
GtkIconSize button_size;
|
|
|
|
|
|
|
|
gtk_widget_style_get (widget,
|
|
|
|
"button-relief", &button_relief,
|
|
|
|
"button-icon-size", &button_size,
|
|
|
|
NULL);
|
|
|
|
|
|
|
|
gtk_button_set_relief (GTK_BUTTON (view->priv->link_button),
|
|
|
|
button_relief);
|
|
|
|
|
|
|
|
image = gtk_button_get_image (GTK_BUTTON (view->priv->link_button));
|
|
|
|
|
|
|
|
gtk_image_get_icon_name (GTK_IMAGE (image), &old_icon_name, &old_size);
|
|
|
|
|
|
|
|
if (button_size != old_size)
|
|
|
|
{
|
|
|
|
gchar *icon_name;
|
|
|
|
|
|
|
|
/* Changing the link button in dockable button box. */
|
|
|
|
icon_name = g_strdup (old_icon_name);
|
|
|
|
gtk_image_set_from_icon_name (GTK_IMAGE (image),
|
|
|
|
icon_name, button_size);
|
|
|
|
g_free (icon_name);
|
|
|
|
|
|
|
|
/* Changing the new link button inside the popover. */
|
|
|
|
image = gtk_button_get_image (GTK_BUTTON (view->priv->new_link_button));
|
|
|
|
gtk_image_get_icon_name (GTK_IMAGE (image), &old_icon_name, &old_size);
|
|
|
|
icon_name = g_strdup (old_icon_name);
|
|
|
|
gtk_image_set_from_icon_name (GTK_IMAGE (image),
|
|
|
|
icon_name, button_size);
|
|
|
|
g_free (icon_name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* PikaContainerView methods */
|
|
|
|
|
|
|
|
static void
|
|
|
|
pika_layer_tree_view_set_container (PikaContainerView *view,
|
|
|
|
PikaContainer *container)
|
|
|
|
{
|
|
|
|
PikaLayerTreeView *layer_view = PIKA_LAYER_TREE_VIEW (view);
|
|
|
|
PikaContainer *old_container;
|
|
|
|
|
|
|
|
old_container = pika_container_view_get_container (view);
|
|
|
|
|
|
|
|
if (old_container)
|
|
|
|
{
|
|
|
|
pika_tree_handler_disconnect (layer_view->priv->mode_changed_handler);
|
|
|
|
layer_view->priv->mode_changed_handler = NULL;
|
|
|
|
|
|
|
|
pika_tree_handler_disconnect (layer_view->priv->opacity_changed_handler);
|
|
|
|
layer_view->priv->opacity_changed_handler = NULL;
|
|
|
|
|
|
|
|
pika_tree_handler_disconnect (layer_view->priv->mask_changed_handler);
|
|
|
|
layer_view->priv->mask_changed_handler = NULL;
|
|
|
|
|
|
|
|
pika_tree_handler_disconnect (layer_view->priv->alpha_changed_handler);
|
|
|
|
layer_view->priv->alpha_changed_handler = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
parent_view_iface->set_container (view, container);
|
|
|
|
|
|
|
|
if (container)
|
|
|
|
{
|
|
|
|
layer_view->priv->mode_changed_handler =
|
|
|
|
pika_tree_handler_connect (container, "mode-changed",
|
|
|
|
G_CALLBACK (pika_layer_tree_view_layer_signal_handler),
|
|
|
|
view);
|
|
|
|
|
|
|
|
layer_view->priv->opacity_changed_handler =
|
|
|
|
pika_tree_handler_connect (container, "opacity-changed",
|
|
|
|
G_CALLBACK (pika_layer_tree_view_layer_signal_handler),
|
|
|
|
view);
|
|
|
|
|
|
|
|
layer_view->priv->mask_changed_handler =
|
|
|
|
pika_tree_handler_connect (container, "mask-changed",
|
|
|
|
G_CALLBACK (pika_layer_tree_view_mask_changed),
|
|
|
|
view);
|
|
|
|
|
|
|
|
layer_view->priv->alpha_changed_handler =
|
|
|
|
pika_tree_handler_connect (container, "alpha-changed",
|
|
|
|
G_CALLBACK (pika_layer_tree_view_alpha_changed),
|
|
|
|
view);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
typedef struct
|
|
|
|
{
|
|
|
|
gint mask_column;
|
|
|
|
PikaContext *context;
|
|
|
|
} SetContextForeachData;
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
pika_layer_tree_view_set_context_foreach (GtkTreeModel *model,
|
|
|
|
GtkTreePath *path,
|
|
|
|
GtkTreeIter *iter,
|
|
|
|
gpointer data)
|
|
|
|
{
|
|
|
|
SetContextForeachData *context_data = data;
|
|
|
|
PikaViewRenderer *renderer;
|
|
|
|
|
|
|
|
gtk_tree_model_get (model, iter,
|
|
|
|
context_data->mask_column, &renderer,
|
|
|
|
-1);
|
|
|
|
|
|
|
|
if (renderer)
|
|
|
|
{
|
|
|
|
pika_view_renderer_set_context (renderer, context_data->context);
|
|
|
|
|
|
|
|
g_object_unref (renderer);
|
|
|
|
}
|
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
pika_layer_tree_view_set_context (PikaContainerView *view,
|
|
|
|
PikaContext *context)
|
|
|
|
{
|
|
|
|
PikaContainerTreeView *tree_view = PIKA_CONTAINER_TREE_VIEW (view);
|
|
|
|
PikaLayerTreeView *layer_view = PIKA_LAYER_TREE_VIEW (view);
|
|
|
|
PikaContext *old_context;
|
|
|
|
|
|
|
|
old_context = pika_container_view_get_context (view);
|
|
|
|
|
|
|
|
if (old_context)
|
|
|
|
{
|
|
|
|
g_signal_handlers_disconnect_by_func (old_context->pika->config,
|
|
|
|
G_CALLBACK (pika_layer_tree_view_style_updated),
|
|
|
|
view);
|
|
|
|
}
|
|
|
|
|
|
|
|
parent_view_iface->set_context (view, context);
|
|
|
|
|
|
|
|
if (context)
|
|
|
|
{
|
|
|
|
g_signal_connect_object (context->pika->config,
|
|
|
|
"notify::theme",
|
|
|
|
G_CALLBACK (pika_layer_tree_view_style_updated),
|
|
|
|
view, G_CONNECT_AFTER | G_CONNECT_SWAPPED);
|
|
|
|
g_signal_connect_object (context->pika->config,
|
|
|
|
"notify::override-theme-icon-size",
|
|
|
|
G_CALLBACK (pika_layer_tree_view_style_updated),
|
|
|
|
view, G_CONNECT_AFTER | G_CONNECT_SWAPPED);
|
|
|
|
g_signal_connect_object (context->pika->config,
|
|
|
|
"notify::custom-icon-size",
|
|
|
|
G_CALLBACK (pika_layer_tree_view_style_updated),
|
|
|
|
view, G_CONNECT_AFTER | G_CONNECT_SWAPPED);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (tree_view->model)
|
|
|
|
{
|
|
|
|
SetContextForeachData context_data = { layer_view->priv->model_column_mask,
|
|
|
|
context };
|
|
|
|
|
|
|
|
gtk_tree_model_foreach (tree_view->model,
|
|
|
|
pika_layer_tree_view_set_context_foreach,
|
|
|
|
&context_data);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static gpointer
|
|
|
|
pika_layer_tree_view_insert_item (PikaContainerView *view,
|
|
|
|
PikaViewable *viewable,
|
|
|
|
gpointer parent_insert_data,
|
|
|
|
gint index)
|
|
|
|
{
|
|
|
|
PikaLayerTreeView *layer_view = PIKA_LAYER_TREE_VIEW (view);
|
|
|
|
PikaLayer *layer;
|
|
|
|
GtkTreeIter *iter;
|
|
|
|
|
|
|
|
iter = parent_view_iface->insert_item (view, viewable,
|
|
|
|
parent_insert_data, index);
|
|
|
|
|
|
|
|
layer = PIKA_LAYER (viewable);
|
|
|
|
|
|
|
|
if (! pika_drawable_has_alpha (PIKA_DRAWABLE (layer)))
|
|
|
|
pika_layer_tree_view_alpha_update (layer_view, iter, layer);
|
|
|
|
|
|
|
|
pika_layer_tree_view_mask_update (layer_view, iter, layer);
|
|
|
|
|
|
|
|
if (PIKA_IS_LAYER (viewable) && pika_layer_is_floating_sel (PIKA_LAYER (viewable)))
|
|
|
|
{
|
|
|
|
PikaContainerTreeView *tree_view = PIKA_CONTAINER_TREE_VIEW (view);
|
|
|
|
PikaLayer *floating = PIKA_LAYER (viewable);
|
|
|
|
PikaDrawable *drawable = pika_layer_get_floating_sel_drawable (floating);
|
|
|
|
|
|
|
|
if (PIKA_IS_LAYER_MASK (drawable))
|
|
|
|
{
|
|
|
|
/* Display floating mask in the mask column. */
|
|
|
|
PikaViewRenderer *renderer = NULL;
|
|
|
|
|
|
|
|
gtk_tree_model_get (GTK_TREE_MODEL (tree_view->model), iter,
|
|
|
|
PIKA_CONTAINER_TREE_STORE_COLUMN_RENDERER, &renderer,
|
|
|
|
-1);
|
|
|
|
if (renderer)
|
|
|
|
{
|
|
|
|
gtk_tree_store_set (GTK_TREE_STORE (tree_view->model), iter,
|
|
|
|
layer_view->priv->model_column_mask, renderer,
|
|
|
|
layer_view->priv->model_column_mask_visible, TRUE,
|
|
|
|
PIKA_CONTAINER_TREE_STORE_COLUMN_RENDERER, NULL,
|
|
|
|
-1);
|
|
|
|
g_object_unref (renderer);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return iter;
|
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
pika_layer_tree_view_select_items (PikaContainerView *view,
|
|
|
|
GList *items,
|
|
|
|
GList *paths)
|
|
|
|
{
|
|
|
|
PikaContainerTreeView *tree_view = PIKA_CONTAINER_TREE_VIEW (view);
|
|
|
|
PikaLayerTreeView *layer_view = PIKA_LAYER_TREE_VIEW (view);
|
|
|
|
GList *layers = items;
|
|
|
|
GList *path = paths;
|
|
|
|
gboolean success;
|
|
|
|
|
|
|
|
success = parent_view_iface->select_items (view, items, paths);
|
|
|
|
|
|
|
|
if (layers)
|
|
|
|
{
|
|
|
|
if (success)
|
|
|
|
{
|
2023-10-30 23:55:30 +01:00
|
|
|
if (g_list_length (items) == 1)
|
2023-09-26 00:35:21 +02:00
|
|
|
{
|
2023-10-30 23:55:30 +01:00
|
|
|
GtkTreeIter *iter = NULL;
|
2023-09-26 00:35:21 +02:00
|
|
|
|
2023-10-30 23:55:30 +01:00
|
|
|
iter =
|
|
|
|
pika_container_view_lookup (PIKA_CONTAINER_VIEW (layer_view),
|
|
|
|
PIKA_VIEWABLE (layers->data));
|
|
|
|
|
|
|
|
if (iter)
|
|
|
|
pika_layer_tree_view_update_borders (layer_view, iter);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
for (layers = items, path = paths;
|
|
|
|
layers && path;
|
|
|
|
layers = layers->next, path = path->next)
|
|
|
|
{
|
|
|
|
GtkTreeIter iter;
|
|
|
|
|
|
|
|
gtk_tree_model_get_iter (tree_view->model, &iter,
|
|
|
|
path->data);
|
|
|
|
pika_layer_tree_view_update_borders (layer_view, &iter);
|
|
|
|
}
|
2023-09-26 00:35:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
pika_layer_tree_view_update_options (layer_view, items);
|
|
|
|
pika_layer_tree_view_update_menu (layer_view, items);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (! success)
|
|
|
|
{
|
|
|
|
PikaEditor *editor = PIKA_EDITOR (view);
|
|
|
|
|
|
|
|
/* currently, select_items() only ever fails when there is a floating
|
|
|
|
* selection, which can be committed/canceled through the editor buttons.
|
|
|
|
*/
|
|
|
|
pika_widget_blink (GTK_WIDGET (pika_editor_get_button_box (editor)));
|
|
|
|
}
|
|
|
|
|
|
|
|
return success;
|
|
|
|
}
|
|
|
|
|
|
|
|
typedef struct
|
|
|
|
{
|
|
|
|
gint mask_column;
|
|
|
|
gint view_size;
|
|
|
|
gint border_width;
|
|
|
|
} SetSizeForeachData;
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
pika_layer_tree_view_set_view_size_foreach (GtkTreeModel *model,
|
|
|
|
GtkTreePath *path,
|
|
|
|
GtkTreeIter *iter,
|
|
|
|
gpointer data)
|
|
|
|
{
|
|
|
|
SetSizeForeachData *size_data = data;
|
|
|
|
PikaViewRenderer *renderer;
|
|
|
|
|
|
|
|
gtk_tree_model_get (model, iter,
|
|
|
|
size_data->mask_column, &renderer,
|
|
|
|
-1);
|
|
|
|
|
|
|
|
if (renderer)
|
|
|
|
{
|
|
|
|
pika_view_renderer_set_size (renderer,
|
|
|
|
size_data->view_size,
|
|
|
|
size_data->border_width);
|
|
|
|
|
|
|
|
g_object_unref (renderer);
|
|
|
|
}
|
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
pika_layer_tree_view_set_view_size (PikaContainerView *view)
|
|
|
|
{
|
|
|
|
PikaContainerTreeView *tree_view = PIKA_CONTAINER_TREE_VIEW (view);
|
|
|
|
|
|
|
|
if (tree_view->model)
|
|
|
|
{
|
|
|
|
PikaLayerTreeView *layer_view = PIKA_LAYER_TREE_VIEW (view);
|
|
|
|
SetSizeForeachData size_data;
|
|
|
|
|
|
|
|
size_data.mask_column = layer_view->priv->model_column_mask;
|
|
|
|
|
|
|
|
size_data.view_size =
|
|
|
|
pika_container_view_get_view_size (view, &size_data.border_width);
|
|
|
|
|
|
|
|
gtk_tree_model_foreach (tree_view->model,
|
|
|
|
pika_layer_tree_view_set_view_size_foreach,
|
|
|
|
&size_data);
|
|
|
|
}
|
|
|
|
|
|
|
|
parent_view_iface->set_view_size (view);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* PikaContainerTreeView methods */
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
pika_layer_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 we are dropping a new layer, check if the destination image
|
|
|
|
* has a floating selection.
|
|
|
|
*/
|
|
|
|
if (src_type == PIKA_DND_TYPE_URI_LIST ||
|
|
|
|
src_type == PIKA_DND_TYPE_TEXT_PLAIN ||
|
|
|
|
src_type == PIKA_DND_TYPE_NETSCAPE_URL ||
|
|
|
|
src_type == PIKA_DND_TYPE_COMPONENT ||
|
|
|
|
src_type == PIKA_DND_TYPE_PIXBUF ||
|
|
|
|
g_list_length (src_viewables) > 0)
|
|
|
|
{
|
|
|
|
PikaImage *dest_image = pika_item_tree_view_get_image (PIKA_ITEM_TREE_VIEW (tree_view));
|
|
|
|
|
|
|
|
if (pika_image_get_floating_selection (dest_image))
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 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);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
pika_layer_tree_view_drop_color (PikaContainerTreeView *view,
|
|
|
|
const PikaRGB *color,
|
|
|
|
PikaViewable *dest_viewable,
|
|
|
|
GtkTreeViewDropPosition drop_pos)
|
|
|
|
{
|
|
|
|
if (pika_item_is_text_layer (PIKA_ITEM (dest_viewable)))
|
|
|
|
{
|
|
|
|
pika_text_layer_set (PIKA_TEXT_LAYER (dest_viewable), NULL,
|
|
|
|
"color", color,
|
|
|
|
NULL);
|
|
|
|
pika_image_flush (pika_item_tree_view_get_image (PIKA_ITEM_TREE_VIEW (view)));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
PIKA_CONTAINER_TREE_VIEW_CLASS (parent_class)->drop_color (view, color,
|
|
|
|
dest_viewable,
|
|
|
|
drop_pos);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
pika_layer_tree_view_drop_uri_list (PikaContainerTreeView *view,
|
|
|
|
GList *uri_list,
|
|
|
|
PikaViewable *dest_viewable,
|
|
|
|
GtkTreeViewDropPosition drop_pos)
|
|
|
|
{
|
|
|
|
PikaItemTreeView *item_view = PIKA_ITEM_TREE_VIEW (view);
|
|
|
|
PikaContainerView *cont_view = PIKA_CONTAINER_VIEW (view);
|
|
|
|
PikaImage *image = pika_item_tree_view_get_image (item_view);
|
|
|
|
PikaLayer *parent;
|
|
|
|
gint index;
|
|
|
|
GList *list;
|
|
|
|
|
|
|
|
index = pika_item_tree_view_get_drop_index (item_view, dest_viewable,
|
|
|
|
drop_pos,
|
|
|
|
(PikaViewable **) &parent);
|
|
|
|
|
|
|
|
g_object_ref (image);
|
|
|
|
|
|
|
|
for (list = uri_list; list; list = g_list_next (list))
|
|
|
|
{
|
|
|
|
const gchar *uri = list->data;
|
|
|
|
GFile *file = g_file_new_for_uri (uri);
|
|
|
|
GList *new_layers;
|
|
|
|
PikaPDBStatusType status;
|
|
|
|
GError *error = NULL;
|
|
|
|
|
|
|
|
new_layers = file_open_layers (image->pika,
|
|
|
|
pika_container_view_get_context (cont_view),
|
|
|
|
NULL,
|
|
|
|
image, FALSE,
|
|
|
|
file, PIKA_RUN_INTERACTIVE, NULL,
|
|
|
|
&status, &error);
|
|
|
|
|
|
|
|
if (new_layers)
|
|
|
|
{
|
|
|
|
pika_image_add_layers (image, new_layers, parent, index,
|
|
|
|
0, 0,
|
|
|
|
pika_image_get_width (image),
|
|
|
|
pika_image_get_height (image),
|
|
|
|
_("Drop layers"));
|
|
|
|
|
|
|
|
index += g_list_length (new_layers);
|
|
|
|
|
|
|
|
g_list_free (new_layers);
|
|
|
|
}
|
|
|
|
else if (status != PIKA_PDB_CANCEL)
|
|
|
|
{
|
|
|
|
pika_message (image->pika, G_OBJECT (view), PIKA_MESSAGE_ERROR,
|
|
|
|
_("Opening '%s' failed:\n\n%s"),
|
|
|
|
pika_file_get_utf8_name (file), error->message);
|
|
|
|
g_clear_error (&error);
|
|
|
|
}
|
|
|
|
|
|
|
|
g_object_unref (file);
|
|
|
|
}
|
|
|
|
|
|
|
|
pika_image_flush (image);
|
|
|
|
|
|
|
|
g_object_unref (image);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
pika_layer_tree_view_drop_component (PikaContainerTreeView *tree_view,
|
|
|
|
PikaImage *src_image,
|
|
|
|
PikaChannelType component,
|
|
|
|
PikaViewable *dest_viewable,
|
|
|
|
GtkTreeViewDropPosition drop_pos)
|
|
|
|
{
|
|
|
|
PikaItemTreeView *item_view = PIKA_ITEM_TREE_VIEW (tree_view);
|
|
|
|
PikaImage *image = pika_item_tree_view_get_image (item_view);
|
|
|
|
PikaChannel *channel;
|
|
|
|
PikaItem *new_item;
|
|
|
|
PikaLayer *parent;
|
|
|
|
gint index;
|
|
|
|
const gchar *desc;
|
|
|
|
|
|
|
|
index = pika_item_tree_view_get_drop_index (item_view, dest_viewable,
|
|
|
|
drop_pos,
|
|
|
|
(PikaViewable **) &parent);
|
|
|
|
|
|
|
|
channel = pika_channel_new_from_component (src_image, component, NULL, NULL);
|
|
|
|
|
|
|
|
new_item = pika_item_convert (PIKA_ITEM (channel), image,
|
|
|
|
PIKA_TYPE_LAYER);
|
|
|
|
|
|
|
|
g_object_unref (channel);
|
|
|
|
|
|
|
|
pika_enum_get_value (PIKA_TYPE_CHANNEL_TYPE, component,
|
|
|
|
NULL, NULL, &desc, NULL);
|
|
|
|
pika_object_take_name (PIKA_OBJECT (new_item),
|
|
|
|
g_strdup_printf (_("%s Channel Copy"), desc));
|
|
|
|
|
|
|
|
pika_image_add_layer (image, PIKA_LAYER (new_item), parent, index, TRUE);
|
|
|
|
|
|
|
|
pika_image_flush (image);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
pika_layer_tree_view_drop_pixbuf (PikaContainerTreeView *tree_view,
|
|
|
|
GdkPixbuf *pixbuf,
|
|
|
|
PikaViewable *dest_viewable,
|
|
|
|
GtkTreeViewDropPosition drop_pos)
|
|
|
|
{
|
|
|
|
PikaItemTreeView *item_view = PIKA_ITEM_TREE_VIEW (tree_view);
|
|
|
|
PikaImage *image = pika_item_tree_view_get_image (item_view);
|
|
|
|
PikaLayer *new_layer;
|
|
|
|
PikaLayer *parent;
|
|
|
|
gint index;
|
|
|
|
|
|
|
|
index = pika_item_tree_view_get_drop_index (item_view, dest_viewable,
|
|
|
|
drop_pos,
|
|
|
|
(PikaViewable **) &parent);
|
|
|
|
|
|
|
|
new_layer =
|
|
|
|
pika_layer_new_from_pixbuf (pixbuf, image,
|
|
|
|
pika_image_get_layer_format (image, TRUE),
|
|
|
|
_("Dropped Buffer"),
|
|
|
|
PIKA_OPACITY_OPAQUE,
|
|
|
|
pika_image_get_default_new_layer_mode (image));
|
|
|
|
|
|
|
|
pika_image_add_layer (image, new_layer, parent, index, TRUE);
|
|
|
|
|
|
|
|
pika_image_flush (image);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* PikaItemTreeView methods */
|
|
|
|
|
|
|
|
static void
|
|
|
|
pika_layer_tree_view_set_image (PikaItemTreeView *view,
|
|
|
|
PikaImage *image)
|
|
|
|
{
|
|
|
|
PikaLayerTreeView *layer_view = PIKA_LAYER_TREE_VIEW (view);
|
|
|
|
|
|
|
|
if (pika_item_tree_view_get_image (view))
|
|
|
|
{
|
|
|
|
g_signal_handlers_disconnect_by_func (pika_item_tree_view_get_image (view),
|
|
|
|
pika_layer_tree_view_floating_selection_changed,
|
|
|
|
view);
|
|
|
|
g_signal_handlers_disconnect_by_func (pika_item_tree_view_get_image (view),
|
|
|
|
G_CALLBACK (pika_layer_tree_view_layer_links_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_layer_tree_view_floating_selection_changed),
|
|
|
|
view);
|
|
|
|
g_signal_connect (pika_item_tree_view_get_image (view),
|
|
|
|
"layer-sets-changed",
|
|
|
|
G_CALLBACK (pika_layer_tree_view_layer_links_changed),
|
|
|
|
view);
|
|
|
|
|
|
|
|
/* call pika_layer_tree_view_floating_selection_changed() now, to update
|
|
|
|
* the floating selection's row attributes.
|
|
|
|
*/
|
|
|
|
pika_layer_tree_view_floating_selection_changed (
|
|
|
|
pika_item_tree_view_get_image (view),
|
|
|
|
layer_view);
|
|
|
|
}
|
|
|
|
/* Call this even with no image, allowing to empty the link list. */
|
|
|
|
pika_layer_tree_view_layer_links_changed (pika_item_tree_view_get_image (view),
|
|
|
|
layer_view);
|
|
|
|
|
|
|
|
pika_layer_tree_view_update_highlight (layer_view);
|
|
|
|
}
|
|
|
|
|
|
|
|
static PikaItem *
|
|
|
|
pika_layer_tree_view_item_new (PikaImage *image)
|
|
|
|
{
|
|
|
|
PikaLayer *new_layer;
|
|
|
|
|
|
|
|
pika_image_undo_group_start (image, PIKA_UNDO_GROUP_EDIT_PASTE,
|
|
|
|
_("New Layer"));
|
|
|
|
|
|
|
|
new_layer = pika_layer_new (image,
|
|
|
|
pika_image_get_width (image),
|
|
|
|
pika_image_get_height (image),
|
|
|
|
pika_image_get_layer_format (image, TRUE),
|
|
|
|
NULL,
|
|
|
|
PIKA_OPACITY_OPAQUE,
|
|
|
|
pika_image_get_default_new_layer_mode (image));
|
|
|
|
|
|
|
|
pika_image_add_layer (image, new_layer,
|
|
|
|
PIKA_IMAGE_ACTIVE_PARENT, -1, TRUE);
|
|
|
|
|
|
|
|
pika_image_undo_group_end (image);
|
|
|
|
|
|
|
|
return PIKA_ITEM (new_layer);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* callbacks */
|
|
|
|
|
|
|
|
static void
|
|
|
|
pika_layer_tree_view_floating_selection_changed (PikaImage *image,
|
|
|
|
PikaLayerTreeView *layer_view)
|
|
|
|
{
|
|
|
|
PikaContainerTreeView *tree_view = PIKA_CONTAINER_TREE_VIEW (layer_view);
|
|
|
|
PikaContainerView *view = PIKA_CONTAINER_VIEW (layer_view);
|
|
|
|
PikaLayer *floating_sel;
|
|
|
|
GtkTreeIter *iter;
|
|
|
|
|
|
|
|
floating_sel = pika_image_get_floating_selection (image);
|
|
|
|
|
|
|
|
if (floating_sel)
|
|
|
|
{
|
|
|
|
iter = pika_container_view_lookup (view, (PikaViewable *) floating_sel);
|
|
|
|
|
|
|
|
if (iter)
|
|
|
|
gtk_tree_store_set (GTK_TREE_STORE (tree_view->model), iter,
|
|
|
|
PIKA_CONTAINER_TREE_STORE_COLUMN_NAME_ATTRIBUTES,
|
|
|
|
layer_view->priv->italic_attrs,
|
|
|
|
-1);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
GList *all_layers;
|
|
|
|
GList *list;
|
|
|
|
|
|
|
|
all_layers = pika_image_get_layer_list (image);
|
|
|
|
|
|
|
|
for (list = all_layers; list; list = g_list_next (list))
|
|
|
|
{
|
|
|
|
PikaDrawable *drawable = list->data;
|
|
|
|
|
|
|
|
iter = pika_container_view_lookup (view, (PikaViewable *) drawable);
|
|
|
|
|
|
|
|
if (iter)
|
|
|
|
gtk_tree_store_set (GTK_TREE_STORE (tree_view->model), iter,
|
|
|
|
PIKA_CONTAINER_TREE_STORE_COLUMN_NAME_ATTRIBUTES,
|
|
|
|
pika_drawable_has_alpha (drawable) ?
|
|
|
|
NULL : layer_view->priv->bold_attrs,
|
|
|
|
-1);
|
|
|
|
}
|
|
|
|
|
|
|
|
g_list_free (all_layers);
|
|
|
|
}
|
|
|
|
|
|
|
|
pika_layer_tree_view_update_highlight (layer_view);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
pika_layer_tree_view_layer_links_changed (PikaImage *image,
|
|
|
|
PikaLayerTreeView *view)
|
|
|
|
{
|
|
|
|
GtkWidget *grid;
|
|
|
|
GtkWidget *label;
|
|
|
|
GtkWidget *event_box;
|
|
|
|
GtkWidget *icon;
|
|
|
|
GtkSizeGroup *label_size;
|
|
|
|
GList *links;
|
|
|
|
GList *iter;
|
|
|
|
|
|
|
|
gtk_container_foreach (GTK_CONTAINER (view->priv->link_list),
|
|
|
|
(GtkCallback) gtk_widget_destroy, NULL);
|
|
|
|
gtk_widget_set_sensitive (view->priv->link_button, image != NULL);
|
|
|
|
|
|
|
|
if (! image)
|
|
|
|
return;
|
|
|
|
|
|
|
|
links = pika_image_get_stored_item_sets (image, PIKA_TYPE_LAYER);
|
|
|
|
|
|
|
|
label_size = gtk_size_group_new (GTK_SIZE_GROUP_BOTH);
|
|
|
|
for (iter = links; iter; iter = iter->next)
|
|
|
|
{
|
|
|
|
PikaSelectMethod method;
|
|
|
|
|
|
|
|
grid = gtk_grid_new ();
|
|
|
|
|
|
|
|
label = gtk_label_new (pika_object_get_name (iter->data));
|
|
|
|
gtk_label_set_xalign (GTK_LABEL (label), 0.0);
|
|
|
|
if (pika_item_list_is_pattern (iter->data, &method))
|
|
|
|
{
|
|
|
|
gchar *display_name;
|
|
|
|
PangoAttrList *attrs;
|
|
|
|
|
|
|
|
display_name = g_strdup_printf ("<small>[%s]</small> %s",
|
|
|
|
method == PIKA_SELECT_PLAIN_TEXT ? _("search") :
|
|
|
|
(method == PIKA_SELECT_GLOB_PATTERN ? _("glob") : _("regexp")),
|
|
|
|
pika_object_get_name (iter->data));
|
|
|
|
gtk_label_set_markup (GTK_LABEL (label), display_name);
|
|
|
|
g_free (display_name);
|
|
|
|
|
|
|
|
attrs = pango_attr_list_new ();
|
|
|
|
pango_attr_list_insert (attrs, pango_attr_style_new (PANGO_STYLE_OBLIQUE));
|
|
|
|
gtk_label_set_attributes (GTK_LABEL (label), attrs);
|
|
|
|
pango_attr_list_unref (attrs);
|
|
|
|
}
|
|
|
|
gtk_widget_set_hexpand (GTK_WIDGET (label), TRUE);
|
|
|
|
gtk_widget_set_halign (GTK_WIDGET (label), GTK_ALIGN_START);
|
|
|
|
gtk_size_group_add_widget (label_size, label);
|
|
|
|
gtk_grid_attach (GTK_GRID (grid), label, 0, 1, 1, 1);
|
|
|
|
gtk_widget_show (label);
|
|
|
|
|
|
|
|
/* I don't use a GtkButton because the minimum size is 16 which is
|
|
|
|
* weird and ugly here. And somehow if I force smaller GtkImage
|
|
|
|
* size then add it to the GtkButton, I still get a giant button
|
|
|
|
* with a small image in it, which is even worse. XXX
|
|
|
|
*/
|
|
|
|
event_box = gtk_event_box_new ();
|
|
|
|
gtk_event_box_set_above_child (GTK_EVENT_BOX (event_box), TRUE);
|
|
|
|
gtk_widget_add_events (event_box, GDK_BUTTON_RELEASE_MASK);
|
|
|
|
g_object_set_data (G_OBJECT (event_box), "link-set", iter->data);
|
|
|
|
g_signal_connect (event_box, "button-release-event",
|
|
|
|
G_CALLBACK (pika_layer_tree_view_unlink_clicked),
|
|
|
|
view);
|
|
|
|
gtk_grid_attach (GTK_GRID (grid), event_box, 2, 0, 1, 1);
|
|
|
|
gtk_widget_show (event_box);
|
|
|
|
|
|
|
|
icon = gtk_image_new_from_icon_name (PIKA_ICON_EDIT_DELETE, GTK_ICON_SIZE_MENU);
|
|
|
|
gtk_image_set_pixel_size (GTK_IMAGE (icon), 10);
|
|
|
|
gtk_container_add (GTK_CONTAINER (event_box), icon);
|
|
|
|
gtk_widget_show (icon);
|
|
|
|
|
|
|
|
/* Now using again an event box on the whole grid, but behind its
|
|
|
|
* child (so that the delete button is processed first. I do it
|
|
|
|
* this way instead of using the "row-activated" of GtkListBox
|
|
|
|
* because this signal does not give us event info, and in
|
|
|
|
* particular modifier state. Yet I want to be able to process
|
|
|
|
* Shift/Ctrl state for logical operations on layer sets.
|
|
|
|
*/
|
|
|
|
event_box = gtk_event_box_new ();
|
|
|
|
gtk_event_box_set_above_child (GTK_EVENT_BOX (event_box), FALSE);
|
|
|
|
gtk_widget_add_events (event_box, GDK_BUTTON_RELEASE_MASK);
|
|
|
|
g_object_set_data (G_OBJECT (event_box), "link-set", iter->data);
|
|
|
|
gtk_container_add (GTK_CONTAINER (event_box), grid);
|
|
|
|
gtk_list_box_prepend (GTK_LIST_BOX (view->priv->link_list), event_box);
|
|
|
|
gtk_widget_show (event_box);
|
|
|
|
|
|
|
|
g_signal_connect (event_box,
|
|
|
|
"button-release-event",
|
|
|
|
G_CALLBACK (pika_layer_tree_view_link_clicked),
|
|
|
|
view);
|
|
|
|
|
|
|
|
gtk_widget_show (grid);
|
|
|
|
}
|
|
|
|
g_object_unref (label_size);
|
|
|
|
gtk_list_box_unselect_all (GTK_LIST_BOX (view->priv->link_list));
|
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
pika_layer_tree_view_link_clicked (GtkWidget *box,
|
|
|
|
GdkEvent *event,
|
|
|
|
PikaLayerTreeView *view)
|
|
|
|
{
|
|
|
|
PikaImage *image;
|
|
|
|
GdkEventButton *bevent = (GdkEventButton *) event;
|
|
|
|
GdkModifierType modifiers;
|
|
|
|
|
|
|
|
image = pika_item_tree_view_get_image (PIKA_ITEM_TREE_VIEW (view));
|
|
|
|
|
|
|
|
g_return_val_if_fail (PIKA_IS_IMAGE (image), FALSE);
|
|
|
|
g_return_val_if_fail (GTK_IS_EVENT_BOX (box), FALSE);
|
|
|
|
|
|
|
|
modifiers = bevent->state & pika_get_all_modifiers_mask ();
|
|
|
|
if (modifiers == GDK_SHIFT_MASK)
|
|
|
|
pika_image_add_item_set (image, g_object_get_data (G_OBJECT (box), "link-set"));
|
|
|
|
else if (modifiers == GDK_CONTROL_MASK)
|
|
|
|
pika_image_remove_item_set (image, g_object_get_data (G_OBJECT (box), "link-set"));
|
|
|
|
else if (modifiers == (GDK_CONTROL_MASK | GDK_SHIFT_MASK))
|
|
|
|
pika_image_intersect_item_set (image, g_object_get_data (G_OBJECT (box), "link-set"));
|
|
|
|
else
|
|
|
|
pika_image_select_item_set (image, g_object_get_data (G_OBJECT (box), "link-set"));
|
|
|
|
|
|
|
|
gtk_entry_set_text (GTK_ENTRY (view->priv->link_search_entry), "");
|
|
|
|
/* TODO: if clicking on pattern link, fill in the pattern field? */
|
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
pika_layer_tree_view_link_popover_shown (GtkPopover *popover,
|
|
|
|
PikaLayerTreeView *view)
|
|
|
|
{
|
|
|
|
PikaImage *image;
|
|
|
|
PikaSelectMethod pattern_syntax;
|
|
|
|
|
|
|
|
image = pika_item_tree_view_get_image (PIKA_ITEM_TREE_VIEW (view));
|
|
|
|
|
|
|
|
if (! image)
|
|
|
|
{
|
|
|
|
gtk_widget_set_tooltip_text (view->priv->link_search_entry,
|
|
|
|
_("Select layers by text search"));
|
|
|
|
gtk_entry_set_placeholder_text (GTK_ENTRY (view->priv->link_search_entry),
|
|
|
|
_("Text search"));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
g_object_get (image->pika->config,
|
|
|
|
"items-select-method", &pattern_syntax,
|
|
|
|
NULL);
|
|
|
|
switch (pattern_syntax)
|
|
|
|
{
|
|
|
|
case PIKA_SELECT_PLAIN_TEXT:
|
|
|
|
gtk_widget_set_tooltip_text (view->priv->link_search_entry,
|
|
|
|
_("Select layers by text search"));
|
|
|
|
gtk_entry_set_placeholder_text (GTK_ENTRY (view->priv->link_search_entry),
|
|
|
|
_("Text search"));
|
|
|
|
break;
|
|
|
|
case PIKA_SELECT_GLOB_PATTERN:
|
|
|
|
gtk_widget_set_tooltip_text (view->priv->link_search_entry,
|
|
|
|
_("Select layers by glob patterns"));
|
|
|
|
gtk_entry_set_placeholder_text (GTK_ENTRY (view->priv->link_search_entry),
|
|
|
|
_("Glob pattern search"));
|
|
|
|
break;
|
|
|
|
case PIKA_SELECT_REGEX_PATTERN:
|
|
|
|
gtk_widget_set_tooltip_text (view->priv->link_search_entry,
|
|
|
|
_("Select layers by regular expressions"));
|
|
|
|
gtk_entry_set_placeholder_text (GTK_ENTRY (view->priv->link_search_entry),
|
|
|
|
_("Regular Expression search"));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
pika_layer_tree_view_search_key_release (GtkWidget *widget,
|
|
|
|
GdkEventKey *event,
|
|
|
|
PikaLayerTreeView *view)
|
|
|
|
{
|
|
|
|
PikaImage *image;
|
|
|
|
const gchar *pattern;
|
|
|
|
PikaSelectMethod pattern_syntax;
|
|
|
|
|
|
|
|
if (event->keyval == GDK_KEY_Escape ||
|
|
|
|
event->keyval == GDK_KEY_Return ||
|
|
|
|
event->keyval == GDK_KEY_KP_Enter ||
|
|
|
|
event->keyval == GDK_KEY_ISO_Enter)
|
|
|
|
{
|
|
|
|
if (event->state & GDK_SHIFT_MASK)
|
|
|
|
{
|
|
|
|
if (pika_layer_tree_view_new_link_clicked (view))
|
|
|
|
gtk_widget_hide (view->priv->link_popover);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
gtk_widget_hide (view->priv->link_popover);
|
|
|
|
}
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
gtk_entry_set_attributes (GTK_ENTRY (view->priv->link_search_entry),
|
|
|
|
NULL);
|
|
|
|
|
|
|
|
image = pika_item_tree_view_get_image (PIKA_ITEM_TREE_VIEW (view));
|
|
|
|
g_clear_object (&view->priv->link_pattern_set);
|
|
|
|
|
|
|
|
if (! image)
|
|
|
|
return TRUE;
|
|
|
|
|
|
|
|
g_object_get (image->pika->config,
|
|
|
|
"items-select-method", &pattern_syntax,
|
|
|
|
NULL);
|
|
|
|
pattern = gtk_entry_get_text (GTK_ENTRY (view->priv->link_search_entry));
|
|
|
|
if (pattern && strlen (pattern) > 0)
|
|
|
|
{
|
|
|
|
GList *items;
|
|
|
|
GError *error = NULL;
|
|
|
|
|
|
|
|
gtk_entry_set_text (GTK_ENTRY (view->priv->link_entry), "");
|
|
|
|
gtk_widget_set_sensitive (view->priv->link_entry, FALSE);
|
|
|
|
|
|
|
|
view->priv->link_pattern_set = pika_item_list_pattern_new (image,
|
|
|
|
PIKA_TYPE_LAYER,
|
|
|
|
pattern_syntax,
|
|
|
|
pattern);
|
|
|
|
items = pika_item_list_get_items (view->priv->link_pattern_set, &error);
|
|
|
|
if (error)
|
|
|
|
{
|
|
|
|
/* Invalid regular expression. */
|
|
|
|
PangoAttrList *attrs = pango_attr_list_new ();
|
|
|
|
gchar *tooltip;
|
|
|
|
|
|
|
|
pango_attr_list_insert (attrs, pango_attr_strikethrough_new (TRUE));
|
|
|
|
tooltip = g_strdup_printf (_("Invalid regular expression: %s\n"),
|
|
|
|
error->message);
|
|
|
|
gtk_widget_set_tooltip_text (view->priv->link_search_entry,
|
|
|
|
tooltip);
|
|
|
|
pika_image_set_selected_layers (image, NULL);
|
|
|
|
gtk_entry_set_attributes (GTK_ENTRY (view->priv->link_search_entry),
|
|
|
|
attrs);
|
|
|
|
g_free (tooltip);
|
|
|
|
g_error_free (error);
|
|
|
|
pango_attr_list_unref (attrs);
|
|
|
|
|
|
|
|
g_clear_object (&view->priv->link_pattern_set);
|
|
|
|
}
|
|
|
|
else if (items == NULL)
|
|
|
|
{
|
|
|
|
/* Pattern does not match any results. */
|
|
|
|
pika_image_set_selected_layers (image, NULL);
|
|
|
|
pika_widget_blink (view->priv->link_search_entry);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
pika_image_set_selected_layers (image, items);
|
|
|
|
g_list_free (items);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
gtk_widget_set_sensitive (view->priv->link_entry, TRUE);
|
|
|
|
}
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
pika_layer_tree_view_start_interactive_search (GtkTreeView *tree_view,
|
|
|
|
PikaLayerTreeView *layer_view)
|
|
|
|
{
|
|
|
|
gtk_widget_show (layer_view->priv->link_popover);
|
|
|
|
gtk_widget_grab_focus (layer_view->priv->link_search_entry);
|
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
pika_layer_tree_view_new_link_exit (PikaLayerTreeView *view)
|
|
|
|
{
|
|
|
|
if (pika_layer_tree_view_new_link_clicked (view))
|
|
|
|
gtk_widget_hide (view->priv->link_popover);
|
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
pika_layer_tree_view_new_link_clicked (PikaLayerTreeView *view)
|
|
|
|
{
|
|
|
|
PikaImage *image;
|
|
|
|
const gchar *name;
|
|
|
|
|
|
|
|
image = pika_item_tree_view_get_image (PIKA_ITEM_TREE_VIEW (view));
|
|
|
|
|
|
|
|
if (! image)
|
|
|
|
return TRUE;
|
|
|
|
|
|
|
|
name = gtk_entry_get_text (GTK_ENTRY (view->priv->link_entry));
|
|
|
|
if (name && strlen (name) > 0)
|
|
|
|
{
|
|
|
|
PikaItemList *set;
|
|
|
|
|
|
|
|
set = pika_item_list_named_new (image, PIKA_TYPE_LAYER, name, NULL);
|
|
|
|
if (set)
|
|
|
|
{
|
|
|
|
pika_image_store_item_set (image, set);
|
|
|
|
gtk_entry_set_text (GTK_ENTRY (view->priv->link_entry), "");
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* No existing selection. */
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (view->priv->link_pattern_set != NULL)
|
|
|
|
{
|
|
|
|
pika_image_store_item_set (image, view->priv->link_pattern_set);
|
|
|
|
view->priv->link_pattern_set = NULL;
|
|
|
|
gtk_entry_set_text (GTK_ENTRY (view->priv->link_search_entry), "");
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
pika_widget_blink (view->priv->link_entry);
|
|
|
|
pika_widget_blink (view->priv->link_search_entry);
|
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
pika_layer_tree_view_unlink_clicked (GtkWidget *widget,
|
|
|
|
GdkEvent *event,
|
|
|
|
PikaLayerTreeView *view)
|
|
|
|
{
|
|
|
|
PikaImage *image;
|
|
|
|
|
|
|
|
image = pika_item_tree_view_get_image (PIKA_ITEM_TREE_VIEW (view));
|
|
|
|
|
|
|
|
g_return_val_if_fail (PIKA_IS_IMAGE (image), FALSE);
|
|
|
|
|
|
|
|
pika_image_unlink_item_set (image,
|
|
|
|
g_object_get_data (G_OBJECT (widget),
|
|
|
|
"link-set"));
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Paint Mode, Opacity and Lock alpha callbacks */
|
|
|
|
|
|
|
|
#define BLOCK() \
|
|
|
|
g_signal_handlers_block_by_func (layer, \
|
|
|
|
pika_layer_tree_view_layer_signal_handler, view)
|
|
|
|
|
|
|
|
#define UNBLOCK() \
|
|
|
|
g_signal_handlers_unblock_by_func (layer, \
|
|
|
|
pika_layer_tree_view_layer_signal_handler, view)
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
pika_layer_tree_view_layer_mode_box_callback (GtkWidget *widget,
|
|
|
|
const GParamSpec *pspec,
|
|
|
|
PikaLayerTreeView *view)
|
|
|
|
{
|
|
|
|
PikaImage *image;
|
|
|
|
GList *layers = NULL;
|
|
|
|
GList *iter;
|
|
|
|
PikaUndo *undo;
|
|
|
|
gboolean push_undo = TRUE;
|
|
|
|
gint n_layers = 0;
|
|
|
|
PikaLayerMode mode;
|
|
|
|
|
|
|
|
image = pika_item_tree_view_get_image (PIKA_ITEM_TREE_VIEW (view));
|
|
|
|
mode = pika_layer_mode_box_get_mode (PIKA_LAYER_MODE_BOX (widget));
|
|
|
|
undo = pika_image_undo_can_compress (image, PIKA_TYPE_ITEM_UNDO,
|
|
|
|
PIKA_UNDO_LAYER_MODE);
|
|
|
|
|
|
|
|
if (image)
|
|
|
|
layers = PIKA_ITEM_TREE_VIEW_GET_CLASS (view)->get_selected_items (image);
|
|
|
|
|
|
|
|
for (iter = layers; iter; iter = iter->next)
|
|
|
|
{
|
|
|
|
if (pika_layer_get_mode (iter->data) != mode)
|
|
|
|
{
|
|
|
|
n_layers++;
|
|
|
|
|
|
|
|
if (undo && PIKA_ITEM_UNDO (undo)->item == PIKA_ITEM (iter->data))
|
|
|
|
push_undo = FALSE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (n_layers > 1)
|
|
|
|
{
|
|
|
|
/* Don't compress mode undos with more than 1 layer changed. */
|
|
|
|
push_undo = TRUE;
|
|
|
|
|
|
|
|
pika_image_undo_group_start (image, PIKA_UNDO_GROUP_LAYER_OPACITY,
|
|
|
|
_("Set layers mode"));
|
|
|
|
}
|
|
|
|
|
|
|
|
for (iter = layers; iter; iter = iter->next)
|
|
|
|
{
|
|
|
|
PikaLayer *layer = iter->data;
|
|
|
|
|
|
|
|
if (pika_layer_get_mode (layer) != mode)
|
|
|
|
{
|
|
|
|
BLOCK();
|
|
|
|
pika_layer_set_mode (layer, (PikaLayerMode) mode, push_undo);
|
|
|
|
UNBLOCK();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
pika_image_flush (image);
|
|
|
|
|
|
|
|
if (! push_undo)
|
|
|
|
pika_undo_refresh_preview (undo, pika_container_view_get_context (PIKA_CONTAINER_VIEW (view)));
|
|
|
|
|
|
|
|
if (n_layers > 1)
|
|
|
|
pika_image_undo_group_end (image);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
pika_layer_tree_view_opacity_scale_changed (GtkAdjustment *adjustment,
|
|
|
|
PikaLayerTreeView *view)
|
|
|
|
{
|
|
|
|
PikaImage *image;
|
|
|
|
GList *layers;
|
|
|
|
GList *iter;
|
|
|
|
PikaUndo *undo;
|
|
|
|
gboolean push_undo = TRUE;
|
|
|
|
gint n_layers = 0;
|
|
|
|
gdouble opacity;
|
|
|
|
|
|
|
|
image = pika_item_tree_view_get_image (PIKA_ITEM_TREE_VIEW (view));
|
|
|
|
layers = PIKA_ITEM_TREE_VIEW_GET_CLASS (view)->get_selected_items (image);
|
|
|
|
undo = pika_image_undo_can_compress (image, PIKA_TYPE_ITEM_UNDO,
|
|
|
|
PIKA_UNDO_LAYER_OPACITY);
|
|
|
|
opacity = gtk_adjustment_get_value (adjustment) / 100.0;
|
|
|
|
|
|
|
|
for (iter = layers; iter; iter = iter->next)
|
|
|
|
{
|
|
|
|
if (pika_layer_get_opacity (iter->data) != opacity)
|
|
|
|
{
|
|
|
|
n_layers++;
|
|
|
|
|
|
|
|
if (undo && PIKA_ITEM_UNDO (undo)->item == PIKA_ITEM (iter->data))
|
|
|
|
push_undo = FALSE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (n_layers > 1)
|
|
|
|
{
|
|
|
|
/* Don't compress opacity undos with more than 1 layer changed. */
|
|
|
|
push_undo = TRUE;
|
|
|
|
|
|
|
|
pika_image_undo_group_start (image, PIKA_UNDO_GROUP_LAYER_OPACITY,
|
|
|
|
_("Set layers opacity"));
|
|
|
|
}
|
|
|
|
|
|
|
|
for (iter = layers; iter; iter = iter->next)
|
|
|
|
{
|
|
|
|
PikaLayer *layer = iter->data;
|
|
|
|
|
|
|
|
if (pika_layer_get_opacity (layer) != opacity)
|
|
|
|
{
|
|
|
|
BLOCK();
|
|
|
|
pika_layer_set_opacity (layer, opacity, push_undo);
|
|
|
|
UNBLOCK();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
pika_image_flush (image);
|
|
|
|
|
|
|
|
if (! push_undo)
|
|
|
|
pika_undo_refresh_preview (undo, pika_container_view_get_context (PIKA_CONTAINER_VIEW (view)));
|
|
|
|
|
|
|
|
if (n_layers > 1)
|
|
|
|
pika_image_undo_group_end (image);
|
|
|
|
}
|
|
|
|
|
|
|
|
#undef BLOCK
|
|
|
|
#undef UNBLOCK
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
pika_layer_tree_view_layer_signal_handler (PikaLayer *layer,
|
|
|
|
PikaLayerTreeView *view)
|
|
|
|
{
|
|
|
|
PikaItemTreeView *item_view = PIKA_ITEM_TREE_VIEW (view);
|
|
|
|
GList *selected_layers;
|
|
|
|
|
|
|
|
selected_layers =
|
|
|
|
PIKA_ITEM_TREE_VIEW_GET_CLASS (view)->get_selected_items (pika_item_tree_view_get_image (item_view));
|
|
|
|
|
|
|
|
if (g_list_find (selected_layers, layer))
|
|
|
|
pika_layer_tree_view_update_options (view, selected_layers);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#define BLOCK(object,function) \
|
|
|
|
g_signal_handlers_block_by_func ((object), (function), view)
|
|
|
|
|
|
|
|
#define UNBLOCK(object,function) \
|
|
|
|
g_signal_handlers_unblock_by_func ((object), (function), view)
|
|
|
|
|
|
|
|
static void
|
|
|
|
pika_layer_tree_view_update_options (PikaLayerTreeView *view,
|
|
|
|
GList *layers)
|
|
|
|
{
|
|
|
|
GList *iter;
|
|
|
|
PikaLayerMode mode = PIKA_LAYER_MODE_SEPARATOR;
|
|
|
|
PikaLayerModeContext context = 0;
|
|
|
|
/*gboolean inconsistent_opacity = FALSE;*/
|
|
|
|
gboolean inconsistent_mode = FALSE;
|
|
|
|
gdouble opacity = -1.0;
|
|
|
|
|
|
|
|
for (iter = layers; iter; iter = iter->next)
|
|
|
|
{
|
|
|
|
#if 0
|
|
|
|
if (opacity != -1.0 &&
|
|
|
|
opacity != pika_layer_get_opacity (iter->data))
|
|
|
|
/* We don't really have a way to show an inconsistent
|
|
|
|
* PikaSpinScale. This is currently unused.
|
|
|
|
*/
|
|
|
|
inconsistent_opacity = TRUE;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
opacity = pika_layer_get_opacity (iter->data);
|
|
|
|
|
|
|
|
if (pika_viewable_get_children (iter->data))
|
|
|
|
context |= PIKA_LAYER_MODE_CONTEXT_GROUP;
|
|
|
|
else
|
|
|
|
context |= PIKA_LAYER_MODE_CONTEXT_LAYER;
|
|
|
|
|
|
|
|
if (mode != PIKA_LAYER_MODE_SEPARATOR &&
|
|
|
|
mode != pika_layer_get_mode (iter->data))
|
|
|
|
inconsistent_mode = TRUE;
|
|
|
|
|
|
|
|
mode = pika_layer_get_mode (iter->data);
|
|
|
|
}
|
|
|
|
if (opacity == -1.0)
|
|
|
|
opacity = 1.0;
|
|
|
|
|
|
|
|
if (inconsistent_mode)
|
|
|
|
mode = PIKA_LAYER_MODE_SEPARATOR;
|
|
|
|
|
|
|
|
if (! context)
|
|
|
|
context = PIKA_LAYER_MODE_CONTEXT_LAYER;
|
|
|
|
|
|
|
|
BLOCK (view->priv->layer_mode_box,
|
|
|
|
pika_layer_tree_view_layer_mode_box_callback);
|
|
|
|
|
|
|
|
pika_layer_mode_box_set_context (PIKA_LAYER_MODE_BOX (view->priv->layer_mode_box),
|
|
|
|
context);
|
|
|
|
pika_layer_mode_box_set_mode (PIKA_LAYER_MODE_BOX (view->priv->layer_mode_box),
|
|
|
|
mode);
|
|
|
|
|
|
|
|
UNBLOCK (view->priv->layer_mode_box,
|
|
|
|
pika_layer_tree_view_layer_mode_box_callback);
|
|
|
|
|
|
|
|
if (opacity * 100.0 !=
|
|
|
|
gtk_adjustment_get_value (view->priv->opacity_adjustment))
|
|
|
|
{
|
|
|
|
BLOCK (view->priv->opacity_adjustment,
|
|
|
|
pika_layer_tree_view_opacity_scale_changed);
|
|
|
|
|
|
|
|
gtk_adjustment_set_value (view->priv->opacity_adjustment,
|
|
|
|
opacity * 100.0);
|
|
|
|
|
|
|
|
UNBLOCK (view->priv->opacity_adjustment,
|
|
|
|
pika_layer_tree_view_opacity_scale_changed);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#undef BLOCK
|
|
|
|
#undef UNBLOCK
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
pika_layer_tree_view_update_menu (PikaLayerTreeView *layer_view,
|
|
|
|
GList *layers)
|
|
|
|
{
|
|
|
|
PikaUIManager *ui_manager = pika_editor_get_ui_manager (PIKA_EDITOR (layer_view));
|
|
|
|
PikaActionGroup *group;
|
|
|
|
GList *iter;
|
|
|
|
gboolean have_masks = FALSE;
|
|
|
|
gboolean all_masks_shown = TRUE;
|
|
|
|
gboolean all_masks_disabled = TRUE;
|
|
|
|
|
|
|
|
group = pika_ui_manager_get_action_group (ui_manager, "layers");
|
|
|
|
|
|
|
|
for (iter = layers; iter; iter = iter->next)
|
|
|
|
{
|
|
|
|
if (pika_layer_get_mask (iter->data))
|
|
|
|
{
|
|
|
|
have_masks = TRUE;
|
|
|
|
if (! pika_layer_get_show_mask (iter->data))
|
|
|
|
all_masks_shown = FALSE;
|
|
|
|
if (pika_layer_get_apply_mask (iter->data))
|
|
|
|
all_masks_disabled = FALSE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pika_action_group_set_action_active (group, "layers-mask-show",
|
|
|
|
have_masks && all_masks_shown);
|
|
|
|
pika_action_group_set_action_active (group, "layers-mask-disable",
|
|
|
|
have_masks && all_masks_disabled);
|
|
|
|
|
|
|
|
/* Only one layer mask at a time can be edited. */
|
|
|
|
pika_action_group_set_action_active (group, "layers-mask-edit",
|
|
|
|
g_list_length (layers) == 1 &&
|
|
|
|
pika_layer_get_mask (layers->data) &&
|
|
|
|
pika_layer_get_edit_mask (layers->data));
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
pika_layer_tree_view_update_highlight (PikaLayerTreeView *layer_view)
|
|
|
|
{
|
|
|
|
PikaItemTreeView *item_view = PIKA_ITEM_TREE_VIEW (layer_view);
|
|
|
|
PikaImage *image = pika_item_tree_view_get_image (item_view);
|
|
|
|
PikaLayer *floating_sel = NULL;
|
|
|
|
GtkReliefStyle default_relief;
|
|
|
|
|
|
|
|
if (image)
|
|
|
|
floating_sel = pika_image_get_floating_selection (image);
|
|
|
|
|
|
|
|
gtk_widget_style_get (GTK_WIDGET (layer_view),
|
|
|
|
"button-relief", &default_relief,
|
|
|
|
NULL);
|
|
|
|
|
|
|
|
pika_button_set_suggested (pika_item_tree_view_get_new_button (item_view),
|
|
|
|
floating_sel &&
|
|
|
|
! PIKA_IS_CHANNEL (pika_layer_get_floating_sel_drawable (floating_sel)),
|
|
|
|
default_relief);
|
|
|
|
|
|
|
|
pika_button_set_destructive (pika_item_tree_view_get_delete_button (item_view),
|
|
|
|
floating_sel != NULL,
|
|
|
|
default_relief);
|
|
|
|
|
|
|
|
pika_button_set_suggested (layer_view->priv->anchor_button,
|
|
|
|
floating_sel != NULL,
|
|
|
|
default_relief);
|
|
|
|
if (floating_sel != NULL)
|
|
|
|
{
|
|
|
|
if (PIKA_IS_LAYER_MASK (pika_layer_get_floating_sel_drawable (floating_sel)))
|
|
|
|
gtk_widget_set_tooltip_text (layer_view->priv->anchor_button,
|
|
|
|
C_("layers-action", "Anchor the floating mask"));
|
|
|
|
else
|
|
|
|
gtk_widget_set_tooltip_text (layer_view->priv->anchor_button,
|
|
|
|
C_("layers-action", "Anchor the floating layer"));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
gtk_widget_set_tooltip_text (layer_view->priv->anchor_button,
|
|
|
|
C_("layers-action", "Anchor the floating layer or mask"));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Layer Mask callbacks */
|
|
|
|
|
|
|
|
static void
|
|
|
|
pika_layer_tree_view_mask_update (PikaLayerTreeView *layer_view,
|
|
|
|
GtkTreeIter *iter,
|
|
|
|
PikaLayer *layer)
|
|
|
|
{
|
|
|
|
PikaContainerView *view = PIKA_CONTAINER_VIEW (layer_view);
|
|
|
|
PikaContainerTreeView *tree_view = PIKA_CONTAINER_TREE_VIEW (layer_view);
|
|
|
|
PikaLayerMask *mask;
|
|
|
|
PikaViewRenderer *renderer = NULL;
|
|
|
|
gboolean mask_visible = FALSE;
|
|
|
|
|
|
|
|
mask = pika_layer_get_mask (layer);
|
|
|
|
|
|
|
|
if (mask)
|
|
|
|
{
|
|
|
|
GClosure *closure;
|
|
|
|
gint view_size;
|
|
|
|
gint border_width;
|
|
|
|
|
|
|
|
view_size = pika_container_view_get_view_size (view, &border_width);
|
|
|
|
|
|
|
|
mask_visible = TRUE;
|
|
|
|
|
|
|
|
renderer = pika_view_renderer_new (pika_container_view_get_context (view),
|
|
|
|
G_TYPE_FROM_INSTANCE (mask),
|
|
|
|
view_size, border_width,
|
|
|
|
FALSE);
|
|
|
|
pika_view_renderer_set_viewable (renderer, PIKA_VIEWABLE (mask));
|
|
|
|
|
|
|
|
g_signal_connect (renderer, "update",
|
|
|
|
G_CALLBACK (pika_layer_tree_view_renderer_update),
|
|
|
|
layer_view);
|
|
|
|
|
|
|
|
closure = g_cclosure_new (G_CALLBACK (pika_layer_tree_view_mask_callback),
|
|
|
|
layer_view, NULL);
|
|
|
|
g_object_watch_closure (G_OBJECT (renderer), closure);
|
|
|
|
g_signal_connect_closure (layer, "apply-mask-changed", closure, FALSE);
|
|
|
|
g_signal_connect_closure (layer, "edit-mask-changed", closure, FALSE);
|
|
|
|
g_signal_connect_closure (layer, "show-mask-changed", closure, FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
gtk_tree_store_set (GTK_TREE_STORE (tree_view->model), iter,
|
|
|
|
layer_view->priv->model_column_mask, renderer,
|
|
|
|
layer_view->priv->model_column_mask_visible, mask_visible,
|
|
|
|
-1);
|
|
|
|
|
|
|
|
pika_layer_tree_view_update_borders (layer_view, iter);
|
|
|
|
|
|
|
|
if (renderer)
|
|
|
|
{
|
|
|
|
pika_view_renderer_remove_idle (renderer);
|
|
|
|
g_object_unref (renderer);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
pika_layer_tree_view_mask_changed (PikaLayer *layer,
|
|
|
|
PikaLayerTreeView *layer_view)
|
|
|
|
{
|
|
|
|
PikaContainerView *view = PIKA_CONTAINER_VIEW (layer_view);
|
|
|
|
GtkTreeIter *iter;
|
|
|
|
|
|
|
|
iter = pika_container_view_lookup (view, PIKA_VIEWABLE (layer));
|
|
|
|
|
|
|
|
if (iter)
|
|
|
|
pika_layer_tree_view_mask_update (layer_view, iter, layer);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
pika_layer_tree_view_renderer_update (PikaViewRenderer *renderer,
|
|
|
|
PikaLayerTreeView *layer_view)
|
|
|
|
{
|
|
|
|
PikaContainerView *view = PIKA_CONTAINER_VIEW (layer_view);
|
|
|
|
PikaContainerTreeView *tree_view = PIKA_CONTAINER_TREE_VIEW (layer_view);
|
|
|
|
PikaLayerMask *mask;
|
|
|
|
GtkTreeIter *iter;
|
|
|
|
|
|
|
|
mask = PIKA_LAYER_MASK (renderer->viewable);
|
|
|
|
|
|
|
|
iter = pika_container_view_lookup (view, (PikaViewable *)
|
|
|
|
pika_layer_mask_get_layer (mask));
|
|
|
|
|
|
|
|
if (iter)
|
|
|
|
{
|
|
|
|
GtkTreePath *path;
|
|
|
|
|
|
|
|
path = gtk_tree_model_get_path (tree_view->model, iter);
|
|
|
|
|
|
|
|
gtk_tree_model_row_changed (tree_view->model, path, iter);
|
|
|
|
|
|
|
|
gtk_tree_path_free (path);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
pika_layer_tree_view_update_borders (PikaLayerTreeView *layer_view,
|
|
|
|
GtkTreeIter *iter)
|
|
|
|
{
|
|
|
|
PikaContainerTreeView *tree_view = PIKA_CONTAINER_TREE_VIEW (layer_view);
|
|
|
|
PikaViewRenderer *layer_renderer;
|
|
|
|
PikaViewRenderer *mask_renderer;
|
|
|
|
PikaLayer *layer = NULL;
|
|
|
|
PikaLayerMask *mask = NULL;
|
|
|
|
PikaViewBorderType layer_type = PIKA_VIEW_BORDER_BLACK;
|
|
|
|
|
|
|
|
gtk_tree_model_get (tree_view->model, iter,
|
|
|
|
PIKA_CONTAINER_TREE_STORE_COLUMN_RENDERER, &layer_renderer,
|
|
|
|
layer_view->priv->model_column_mask, &mask_renderer,
|
|
|
|
-1);
|
|
|
|
|
|
|
|
if (layer_renderer)
|
|
|
|
layer = PIKA_LAYER (layer_renderer->viewable);
|
|
|
|
else if (mask_renderer && PIKA_IS_LAYER (mask_renderer->viewable))
|
|
|
|
/* This happens when floating masks are displayed (in the mask column).
|
|
|
|
* In such a case, the mask_renderer will actually be a layer renderer
|
|
|
|
* showing the floating mask.
|
|
|
|
*/
|
|
|
|
layer = PIKA_LAYER (mask_renderer->viewable);
|
|
|
|
|
|
|
|
g_return_if_fail (layer != NULL);
|
|
|
|
|
|
|
|
if (mask_renderer && PIKA_IS_LAYER_MASK (mask_renderer->viewable))
|
|
|
|
mask = PIKA_LAYER_MASK (mask_renderer->viewable);
|
|
|
|
|
|
|
|
if (! mask || (mask && ! pika_layer_get_edit_mask (layer)))
|
|
|
|
layer_type = PIKA_VIEW_BORDER_WHITE;
|
|
|
|
|
|
|
|
if (layer_renderer)
|
|
|
|
pika_view_renderer_set_border_type (layer_renderer, layer_type);
|
|
|
|
|
|
|
|
if (mask)
|
|
|
|
{
|
|
|
|
PikaViewBorderType mask_color = PIKA_VIEW_BORDER_BLACK;
|
|
|
|
|
|
|
|
if (pika_layer_get_show_mask (layer))
|
|
|
|
{
|
|
|
|
mask_color = PIKA_VIEW_BORDER_GREEN;
|
|
|
|
}
|
|
|
|
else if (! pika_layer_get_apply_mask (layer))
|
|
|
|
{
|
|
|
|
mask_color = PIKA_VIEW_BORDER_RED;
|
|
|
|
}
|
|
|
|
else if (pika_layer_get_edit_mask (layer))
|
|
|
|
{
|
|
|
|
mask_color = PIKA_VIEW_BORDER_WHITE;
|
|
|
|
}
|
|
|
|
|
|
|
|
pika_view_renderer_set_border_type (mask_renderer, mask_color);
|
|
|
|
}
|
|
|
|
else if (mask_renderer)
|
|
|
|
{
|
|
|
|
/* Floating mask. */
|
|
|
|
pika_view_renderer_set_border_type (mask_renderer, PIKA_VIEW_BORDER_WHITE);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (layer_renderer)
|
|
|
|
g_object_unref (layer_renderer);
|
|
|
|
|
|
|
|
if (mask_renderer)
|
|
|
|
g_object_unref (mask_renderer);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
pika_layer_tree_view_mask_callback (PikaLayer *layer,
|
|
|
|
PikaLayerTreeView *layer_view)
|
|
|
|
{
|
|
|
|
PikaContainerView *view = PIKA_CONTAINER_VIEW (layer_view);
|
|
|
|
GtkTreeIter *iter;
|
|
|
|
|
|
|
|
iter = pika_container_view_lookup (view, (PikaViewable *) layer);
|
|
|
|
|
|
|
|
pika_layer_tree_view_update_borders (layer_view, iter);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
pika_layer_tree_view_layer_clicked (PikaCellRendererViewable *cell,
|
|
|
|
const gchar *path_str,
|
|
|
|
GdkModifierType state,
|
|
|
|
PikaLayerTreeView *layer_view)
|
|
|
|
{
|
|
|
|
PikaContainerTreeView *tree_view = PIKA_CONTAINER_TREE_VIEW (layer_view);
|
|
|
|
GtkTreePath *path = gtk_tree_path_new_from_string (path_str);
|
|
|
|
GtkTreeIter iter;
|
|
|
|
|
|
|
|
if (gtk_tree_model_get_iter (tree_view->model, &iter, path))
|
|
|
|
{
|
|
|
|
PikaUIManager *ui_manager;
|
|
|
|
PikaActionGroup *group;
|
|
|
|
PikaViewRenderer *renderer;
|
|
|
|
|
|
|
|
ui_manager = pika_editor_get_ui_manager (PIKA_EDITOR (tree_view));
|
|
|
|
group = pika_ui_manager_get_action_group (ui_manager, "layers");
|
|
|
|
|
|
|
|
gtk_tree_model_get (tree_view->model, &iter,
|
|
|
|
PIKA_CONTAINER_TREE_STORE_COLUMN_RENDERER, &renderer,
|
|
|
|
-1);
|
|
|
|
|
|
|
|
if (renderer)
|
|
|
|
{
|
|
|
|
PikaLayer *layer = PIKA_LAYER (renderer->viewable);
|
|
|
|
PikaLayerMask *mask = pika_layer_get_mask (layer);
|
|
|
|
GdkModifierType modifiers = (state & pika_get_all_modifiers_mask ());
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
/* This feature has been removed because it clashes with the
|
|
|
|
* multi-selection (Shift/Ctrl are reserved), and we can't move it to
|
|
|
|
* Alt+ because then it would clash with the alpha-to-selection features
|
|
|
|
* (cf. pika_item_tree_view_item_pre_clicked()).
|
|
|
|
* Just macro-ing them out for now, just in case, but I don't think
|
|
|
|
* there is a chance to revive them as there is no infinite modifiers.
|
|
|
|
*/
|
|
|
|
PikaImage *image;
|
|
|
|
|
|
|
|
image = pika_item_get_image (PIKA_ITEM (layer));
|
|
|
|
|
|
|
|
if ((modifiers & GDK_MOD1_MASK))
|
|
|
|
{
|
|
|
|
/* Alternative functions (Alt key or equivalent) when
|
|
|
|
* clicking on a layer preview. The actions happen only on
|
|
|
|
* the clicked layer, not on selected layers.
|
|
|
|
*
|
|
|
|
* Note: Alt-click is already reserved for Alpha to
|
|
|
|
* selection in PikaItemTreeView.
|
|
|
|
*/
|
|
|
|
if (modifiers == (GDK_MOD1_MASK | GDK_SHIFT_MASK))
|
|
|
|
{
|
|
|
|
/* Alt-Shift-click adds a layer mask with last values */
|
|
|
|
PikaDialogConfig *config;
|
|
|
|
PikaChannel *channel = NULL;
|
|
|
|
|
|
|
|
if (! mask)
|
|
|
|
{
|
|
|
|
config = PIKA_DIALOG_CONFIG (image->pika->config);
|
|
|
|
|
|
|
|
if (config->layer_add_mask_type == PIKA_ADD_MASK_CHANNEL)
|
|
|
|
{
|
|
|
|
channel = pika_image_get_active_channel (image);
|
|
|
|
|
|
|
|
if (! channel)
|
|
|
|
{
|
|
|
|
PikaContainer *channels = pika_image_get_channels (image);
|
|
|
|
|
|
|
|
channel = PIKA_CHANNEL (pika_container_get_first_child (channels));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (! channel)
|
|
|
|
{
|
|
|
|
/* No channel. We cannot perform the add
|
|
|
|
* mask action. */
|
|
|
|
g_message (_("No channels to create a layer mask from."));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (config->layer_add_mask_type != PIKA_ADD_MASK_CHANNEL || channel)
|
|
|
|
{
|
|
|
|
mask = pika_layer_create_mask (layer,
|
|
|
|
config->layer_add_mask_type,
|
|
|
|
channel);
|
|
|
|
|
|
|
|
if (config->layer_add_mask_invert)
|
|
|
|
pika_channel_invert (PIKA_CHANNEL (mask), FALSE);
|
|
|
|
|
|
|
|
pika_layer_add_mask (layer, mask, TRUE, NULL);
|
|
|
|
pika_image_flush (image);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (modifiers == (GDK_MOD1_MASK | GDK_CONTROL_MASK))
|
|
|
|
{
|
|
|
|
/* Alt-Control-click removes a layer mask */
|
|
|
|
if (mask)
|
|
|
|
{
|
|
|
|
pika_layer_apply_mask (layer, PIKA_MASK_DISCARD, TRUE);
|
|
|
|
pika_image_flush (image);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (modifiers == (GDK_MOD1_MASK | GDK_CONTROL_MASK | GDK_SHIFT_MASK))
|
|
|
|
{
|
|
|
|
/* Alt-Shift-Control-click applies a layer mask */
|
|
|
|
if (mask)
|
|
|
|
{
|
|
|
|
pika_layer_apply_mask (layer, PIKA_MASK_APPLY, TRUE);
|
|
|
|
pika_image_flush (image);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
#endif
|
2023-10-30 23:55:30 +01:00
|
|
|
if (! modifiers)
|
2023-09-26 00:35:21 +02:00
|
|
|
{
|
|
|
|
/* Simple clicks (without modifiers) activate the layer */
|
2023-10-30 23:55:30 +01:00
|
|
|
if (mask && pika_layer_get_edit_mask (layer))
|
2023-09-26 00:35:21 +02:00
|
|
|
pika_action_group_set_action_active (group,
|
|
|
|
"layers-mask-edit", FALSE);
|
2023-10-30 23:55:30 +01:00
|
|
|
else
|
|
|
|
pika_layer_tree_view_update_borders (layer_view, &iter);
|
2023-09-26 00:35:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
g_object_unref (renderer);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
gtk_tree_path_free (path);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
pika_layer_tree_view_mask_clicked (PikaCellRendererViewable *cell,
|
|
|
|
const gchar *path_str,
|
|
|
|
GdkModifierType state,
|
|
|
|
PikaLayerTreeView *layer_view)
|
|
|
|
{
|
|
|
|
PikaContainerTreeView *tree_view = PIKA_CONTAINER_TREE_VIEW (layer_view);
|
|
|
|
GtkTreePath *path;
|
|
|
|
GtkTreeIter iter;
|
|
|
|
|
|
|
|
path = gtk_tree_path_new_from_string (path_str);
|
|
|
|
|
|
|
|
if (gtk_tree_model_get_iter (tree_view->model, &iter, path))
|
|
|
|
{
|
|
|
|
PikaViewRenderer *renderer;
|
|
|
|
PikaUIManager *ui_manager;
|
|
|
|
PikaActionGroup *group;
|
|
|
|
|
|
|
|
ui_manager = pika_editor_get_ui_manager (PIKA_EDITOR (tree_view));
|
|
|
|
group = pika_ui_manager_get_action_group (ui_manager, "layers");
|
|
|
|
|
|
|
|
gtk_tree_model_get (tree_view->model, &iter,
|
|
|
|
PIKA_CONTAINER_TREE_STORE_COLUMN_RENDERER, &renderer,
|
|
|
|
-1);
|
|
|
|
|
|
|
|
if (renderer)
|
|
|
|
{
|
|
|
|
PikaLayer *layer = PIKA_LAYER (renderer->viewable);
|
|
|
|
GdkModifierType modifiers = pika_get_all_modifiers_mask ();
|
|
|
|
|
|
|
|
if ((state & GDK_MOD1_MASK))
|
|
|
|
{
|
|
|
|
PikaImage *image;
|
|
|
|
|
|
|
|
image = pika_item_get_image (PIKA_ITEM (layer));
|
|
|
|
|
|
|
|
if ((state & modifiers) == GDK_MOD1_MASK)
|
|
|
|
{
|
|
|
|
/* Alt-click shows/hides a layer mask */
|
|
|
|
pika_layer_set_show_mask (layer,
|
|
|
|
! pika_layer_get_show_mask (layer),
|
|
|
|
TRUE);
|
|
|
|
pika_image_flush (image);
|
|
|
|
}
|
|
|
|
else if ((state & modifiers) == (GDK_MOD1_MASK | GDK_CONTROL_MASK))
|
|
|
|
{
|
|
|
|
/* Alt-Control-click enables/disables a layer mask */
|
|
|
|
pika_layer_set_apply_mask (layer,
|
|
|
|
! pika_layer_get_apply_mask (layer),
|
|
|
|
TRUE);
|
|
|
|
pika_image_flush (image);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (! pika_layer_get_edit_mask (layer))
|
|
|
|
{
|
|
|
|
/* Simple click selects the mask for edition. */
|
|
|
|
pika_action_group_set_action_active (group, "layers-mask-edit",
|
|
|
|
TRUE);
|
|
|
|
}
|
|
|
|
|
|
|
|
g_object_unref (renderer);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
gtk_tree_path_free (path);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* PikaDrawable alpha callbacks */
|
|
|
|
|
|
|
|
static void
|
|
|
|
pika_layer_tree_view_alpha_update (PikaLayerTreeView *view,
|
|
|
|
GtkTreeIter *iter,
|
|
|
|
PikaLayer *layer)
|
|
|
|
{
|
|
|
|
PikaContainerTreeView *tree_view = PIKA_CONTAINER_TREE_VIEW (view);
|
|
|
|
|
|
|
|
gtk_tree_store_set (GTK_TREE_STORE (tree_view->model), iter,
|
|
|
|
PIKA_CONTAINER_TREE_STORE_COLUMN_NAME_ATTRIBUTES,
|
|
|
|
pika_drawable_has_alpha (PIKA_DRAWABLE (layer)) ?
|
|
|
|
NULL : view->priv->bold_attrs,
|
|
|
|
-1);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
pika_layer_tree_view_alpha_changed (PikaLayer *layer,
|
|
|
|
PikaLayerTreeView *layer_view)
|
|
|
|
{
|
|
|
|
PikaContainerView *view = PIKA_CONTAINER_VIEW (layer_view);
|
|
|
|
GtkTreeIter *iter;
|
|
|
|
|
|
|
|
iter = pika_container_view_lookup (view, (PikaViewable *) layer);
|
|
|
|
|
|
|
|
if (iter)
|
|
|
|
{
|
|
|
|
PikaItemTreeView *item_view = PIKA_ITEM_TREE_VIEW (view);
|
|
|
|
|
|
|
|
pika_layer_tree_view_alpha_update (layer_view, iter, layer);
|
|
|
|
|
|
|
|
/* update button states */
|
|
|
|
if (g_list_find (pika_image_get_selected_layers (pika_item_tree_view_get_image (item_view)),
|
|
|
|
layer))
|
|
|
|
pika_container_view_select_items (PIKA_CONTAINER_VIEW (view),
|
|
|
|
pika_image_get_selected_layers (pika_item_tree_view_get_image (item_view)));
|
|
|
|
}
|
|
|
|
}
|