/* 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)
        {
          if (g_list_length (items) == 1)
            {
              GtkTreeIter *iter = NULL;

              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);
                }
            }

          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
          if (! modifiers)
            {
              /* Simple clicks (without modifiers) activate the layer */
              if (mask && pika_layer_get_edit_mask (layer))
                pika_action_group_set_action_active (group,
                                                     "layers-mask-edit", FALSE);
              else
                pika_layer_tree_view_update_borders (layer_view, &iter);
            }

          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)));
    }
}
