Issue #10745: move the "item set" icon above layer list.

- The icon itself was changed to a "search" icon. Though it kinda made sense to
  keep the "link" reference when I first implemented this, as a reference to how
  we used to link items (whereas now we select them together), in the grand
  scheme of things, it's not such a good idea and many won't likely understand.
  But everyone will understand a "search" button. :-)
- It's now next to the label displaying the number of selected items, in the
  item tree view header. This makes much more sense there. Also it makes the
  long list of bottom buttons slighly shorter.
- Finally I make this feature accessible to the channels and paths, by actually
  moving it out of GimpLayerTreeView and into GimpItemTreeView.
This commit is contained in:
Jehan 2024-02-06 21:05:43 +01:00
parent d6edd71c4f
commit 25145b9684
6 changed files with 650 additions and 660 deletions

View File

@ -130,9 +130,7 @@ enum
PARASITE_DETACHED,
COLORMAP_CHANGED,
UNDO_EVENT,
LAYER_SETS_CHANGED,
CHANNEL_SETS_CHANGED,
VECTORS_SETS_CHANGED,
ITEM_SETS_CHANGED,
LAST_SIGNAL
};
@ -587,29 +585,14 @@ gimp_image_class_init (GimpImageClass *klass)
GIMP_TYPE_UNDO_EVENT,
GIMP_TYPE_UNDO);
gimp_image_signals[LAYER_SETS_CHANGED] =
g_signal_new ("layer-sets-changed",
gimp_image_signals[ITEM_SETS_CHANGED] =
g_signal_new ("item-sets-changed",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_FIRST,
G_STRUCT_OFFSET (GimpImageClass, layer_sets_changed),
G_STRUCT_OFFSET (GimpImageClass, item_sets_changed),
NULL, NULL, NULL,
G_TYPE_NONE, 0);
gimp_image_signals[CHANNEL_SETS_CHANGED] =
g_signal_new ("channel-sets-changed",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_FIRST,
G_STRUCT_OFFSET (GimpImageClass, channel_sets_changed),
NULL, NULL, NULL,
G_TYPE_NONE, 0);
gimp_image_signals[VECTORS_SETS_CHANGED] =
g_signal_new ("vectors-sets-changed",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_FIRST,
G_STRUCT_OFFSET (GimpImageClass, vectors_sets_changed),
NULL, NULL, NULL,
G_TYPE_NONE, 0);
G_TYPE_NONE, 1,
G_TYPE_GTYPE);
object_class->constructed = gimp_image_constructed;
object_class->set_property = gimp_image_set_property;
@ -5476,31 +5459,23 @@ gimp_image_store_item_set (GimpImage *image,
GList **stored_sets;
GList *iter;
guint signal;
GType item_type;
g_return_if_fail (GIMP_IS_IMAGE (image));
g_return_if_fail (GIMP_IS_ITEM_LIST (set));
private = GIMP_IMAGE_GET_PRIVATE (image);
if (gimp_item_list_get_item_type (set) == GIMP_TYPE_LAYER)
{
stored_sets = &private->stored_layer_sets;
signal = gimp_image_signals[LAYER_SETS_CHANGED];
}
else if (gimp_item_list_get_item_type (set) == GIMP_TYPE_CHANNEL)
{
stored_sets = &private->stored_channel_sets;
signal = gimp_image_signals[CHANNEL_SETS_CHANGED];
}
else if (gimp_item_list_get_item_type (set) == GIMP_TYPE_VECTORS)
{
stored_sets = &private->stored_vectors_sets;
signal = gimp_image_signals[VECTORS_SETS_CHANGED];
}
signal = gimp_image_signals[ITEM_SETS_CHANGED];
item_type = gimp_item_list_get_item_type (set);
if (item_type == GIMP_TYPE_LAYER)
stored_sets = &private->stored_layer_sets;
else if (item_type == GIMP_TYPE_CHANNEL)
stored_sets = &private->stored_channel_sets;
else if (item_type == GIMP_TYPE_VECTORS)
stored_sets = &private->stored_vectors_sets;
else
{
g_return_if_reached ();
}
g_return_if_reached ();
for (iter = *stored_sets; iter; iter = iter->next)
{
@ -5524,7 +5499,7 @@ gimp_image_store_item_set (GimpImage *image,
}
*stored_sets = g_list_prepend (*stored_sets, set);
g_signal_emit (image, signal, 0);
g_signal_emit (image, signal, 0, item_type);
}
/*
@ -5545,31 +5520,23 @@ gimp_image_unlink_item_set (GimpImage *image,
GList *found;
GList **stored_sets;
guint signal;
GType item_type;
gboolean success;
g_return_val_if_fail (GIMP_IS_IMAGE (image), FALSE);
private = GIMP_IMAGE_GET_PRIVATE (image);
if (gimp_item_list_get_item_type (set) == GIMP_TYPE_LAYER)
{
stored_sets = &private->stored_layer_sets;
signal = gimp_image_signals[LAYER_SETS_CHANGED];
}
else if (gimp_item_list_get_item_type (set) == GIMP_TYPE_CHANNEL)
{
stored_sets = &private->stored_channel_sets;
signal = gimp_image_signals[CHANNEL_SETS_CHANGED];
}
else if (gimp_item_list_get_item_type (set) == GIMP_TYPE_VECTORS)
{
stored_sets = &private->stored_vectors_sets;
signal = gimp_image_signals[VECTORS_SETS_CHANGED];
}
signal = gimp_image_signals[ITEM_SETS_CHANGED];
item_type = gimp_item_list_get_item_type (set);
if (item_type == GIMP_TYPE_LAYER)
stored_sets = &private->stored_layer_sets;
else if (item_type == GIMP_TYPE_CHANNEL)
stored_sets = &private->stored_channel_sets;
else if (item_type == GIMP_TYPE_VECTORS)
stored_sets = &private->stored_vectors_sets;
else
{
g_return_val_if_reached (FALSE);
}
g_return_val_if_reached (FALSE);
found = g_list_find (*stored_sets, set);
success = (found != NULL);
@ -5577,7 +5544,7 @@ gimp_image_unlink_item_set (GimpImage *image,
{
*stored_sets = g_list_delete_link (*stored_sets, found);
g_object_unref (set);
g_signal_emit (image, signal, 0);
g_signal_emit (image, signal, 0, item_type);
}
return success;

View File

@ -103,9 +103,8 @@ struct _GimpImageClass
void (* undo_event) (GimpImage *image,
GimpUndoEvent event,
GimpUndo *undo);
void (* layer_sets_changed) (GimpImage *image);
void (* channel_sets_changed) (GimpImage *image);
void (* vectors_sets_changed) (GimpImage *image);
void (* item_sets_changed) (GimpImage *image,
GType item_type);
};

View File

@ -28,8 +28,6 @@ struct _GimpContainerTreeViewPrivate
GtkCellRenderer *name_cell;
GtkWidget *multi_selection_label;
GList *editable_cells;
gchar *editing_path;

View File

@ -284,14 +284,9 @@ gimp_container_tree_view_constructed (GObject *object)
gimp_container_view_set_dnd_widget (view, GTK_WIDGET (tree_view->view));
tree_view->main_column = gtk_tree_view_column_new ();
tree_view->priv->multi_selection_label = gtk_label_new (NULL);
gtk_label_set_selectable (GTK_LABEL (tree_view->priv->multi_selection_label), TRUE);
gtk_tree_view_column_set_widget (tree_view->main_column,
tree_view->priv->multi_selection_label);
gtk_widget_show (tree_view->priv->multi_selection_label);
gtk_tree_view_insert_column (tree_view->view, tree_view->main_column, 0);
gtk_tree_view_set_expander_column (tree_view->view, tree_view->main_column);
gtk_tree_view_column_set_clickable (tree_view->main_column, FALSE);
gtk_tree_view_set_enable_tree_lines (tree_view->view, TRUE);
tree_view->renderer_cell = gimp_cell_renderer_viewable_new ();
@ -1051,22 +1046,6 @@ gimp_container_tree_view_select_items (GimpContainerView *view,
if (free_paths)
g_list_free_full (paths, (GDestroyNotify) gtk_tree_path_free);
if (g_list_length (items) > 1)
{
gchar *str;
str = g_strdup_printf (ngettext ("%d item selected", "%d items selected",
g_list_length (items)),
g_list_length (items));
gtk_label_set_text (GTK_LABEL (tree_view->priv->multi_selection_label), str);
g_free (str);
gtk_widget_show (tree_view->priv->multi_selection_label);
}
else
{
gtk_widget_hide (tree_view->priv->multi_selection_label);
}
return TRUE;
}

View File

@ -47,6 +47,7 @@
#include "core/gimpimage-undo.h"
#include "core/gimpimage-undo-push.h"
#include "core/gimpitem-exclusive.h"
#include "core/gimpitemlist.h"
#include "core/gimpitemundo.h"
#include "core/gimplist.h"
#include "core/gimptoolinfo.h"
@ -133,6 +134,15 @@ struct _GimpItemTreeViewPrivate
GimpTreeHandler *color_tag_changed_handler;
GimpTreeHandler *filters_changed_handler;
GimpTreeHandler *filters_active_changed_handler;
GtkWidget *multi_selection_label;
GtkWidget *search_button;
GtkWidget *search_popover;
GtkWidget *new_link_button;
GtkWidget *link_list;
GtkWidget *link_entry;
GtkWidget *link_search_entry;
GimpItemList *link_pattern_set;
};
typedef struct
@ -319,6 +329,35 @@ static gint gimp_item_tree_view_get_n_locks (GimpItemTreeView *view,
static void gimp_item_tree_effects_set_sensitive (GimpItemTreeView *view,
gboolean is_sensitive);
/* Functions for the item search/selection feature. */
static void gimp_item_tree_view_create_search_popover (GimpItemTreeView *view,
GtkIconSize button_size);
static void gimp_item_tree_view_search_popover_shown (GtkPopover *popover,
GimpItemTreeView *view);
static void gimp_item_tree_view_floating_selection_changed (GimpImage *image,
GimpItemTreeView *view);
static void gimp_item_tree_view_item_links_changed (GimpImage *image,
GType item_type,
GimpItemTreeView *view);
static gboolean gimp_item_tree_view_link_clicked (GtkWidget *box,
GdkEvent *event,
GimpItemTreeView *view);
static gboolean gimp_item_tree_view_unlink_clicked (GtkWidget *widget,
GdkEvent *event,
GimpItemTreeView *view);
static gboolean gimp_item_tree_view_search_key_release (GtkWidget *widget,
GdkEventKey *event,
GimpItemTreeView *view);
static gboolean gimp_item_tree_view_start_interactive_search (GtkTreeView *tree_view,
GimpItemTreeView *layer_view);
static gboolean gimp_item_tree_view_new_link_clicked (GimpItemTreeView *view);
static void gimp_item_tree_view_new_link_exit (GimpItemTreeView *view);
static gboolean gimp_item_tree_view_search_clicked (GtkWidget *main_column_button,
GdkEventButton *event,
GimpItemTreeView *view);
G_DEFINE_TYPE_WITH_CODE (GimpItemTreeView, gimp_item_tree_view,
GIMP_TYPE_CONTAINER_TREE_VIEW,
@ -457,6 +496,8 @@ gimp_item_tree_view_init (GimpItemTreeView *view)
view->priv->image = NULL;
view->priv->effects_drawable = NULL;
view->priv->effects_filter = NULL;
view->priv->link_pattern_set = NULL;
}
static void
@ -469,6 +510,7 @@ gimp_item_tree_view_constructed (GObject *object)
GtkTreeViewColumn *column;
GtkWidget *image;
GtkWidget *label;
GtkWidget *items_header;
gchar *text;
GtkIconSize button_icon_size = GTK_ICON_SIZE_SMALL_TOOLBAR;
gint pixel_icon_size = 16;
@ -814,6 +856,50 @@ gimp_item_tree_view_constructed (GObject *object)
gtk_widget_set_visible (item_view->priv->effects_options, TRUE);
gtk_widget_set_visible (item_view->priv->effects_filters, TRUE);
}
items_header = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 1);
gtk_widget_set_hexpand (items_header, TRUE);
gtk_widget_set_halign (items_header, GTK_ALIGN_FILL);
gtk_widget_set_margin_end (items_header, 1);
/* Link popover menu. */
item_view->priv->search_button = gtk_image_new_from_icon_name (GIMP_ICON_EDIT_FIND,
button_icon_size);
gtk_widget_set_tooltip_text (item_view->priv->search_button,
_("Select items by patterns and store item sets"));
gtk_box_pack_end (GTK_BOX (items_header), item_view->priv->search_button,
FALSE, FALSE, 2);
gimp_item_tree_view_create_search_popover (item_view, button_icon_size);
gtk_widget_show (item_view->priv->search_button);
/* Search label */
item_view->priv->multi_selection_label = gtk_label_new (NULL);
gtk_label_set_selectable (GTK_LABEL (item_view->priv->multi_selection_label), FALSE);
gtk_widget_set_halign (item_view->priv->multi_selection_label, GTK_ALIGN_START);
gtk_box_pack_end (GTK_BOX (items_header), item_view->priv->multi_selection_label,
FALSE, FALSE, 4);
gtk_widget_show (item_view->priv->multi_selection_label);
gtk_tree_view_column_set_widget (GIMP_CONTAINER_TREE_VIEW (item_view)->main_column, items_header);
gtk_tree_view_column_set_alignment (GIMP_CONTAINER_TREE_VIEW (item_view)->main_column, 1.0);
gtk_tree_view_column_set_clickable (GIMP_CONTAINER_TREE_VIEW (item_view)->main_column, TRUE);
g_signal_connect (gtk_tree_view_column_get_button (GIMP_CONTAINER_TREE_VIEW (item_view)->main_column),
"button-release-event",
G_CALLBACK (gtk_true),
item_view);
/* XXX: I don't use the "clicked" signal of the GtkTreeViewColumn main_column,
* because the event goes through and reaches gimp_container_tree_view_button()
* code, provoking selection of the top item, depending on where we click.
* Catching the signal at the column's button level, we manage to block the
* event from having any side effect other than opening our popup.
*/
g_signal_connect (gtk_tree_view_column_get_button (GIMP_CONTAINER_TREE_VIEW (item_view)->main_column),
"button-press-event",
G_CALLBACK (gimp_item_tree_view_search_clicked),
item_view);
gtk_widget_show (items_header);
}
static void
@ -956,6 +1042,26 @@ gimp_item_tree_view_style_updated (GtkWidget *widget)
g_free (icon_name);
}
gtk_image_get_icon_name (GTK_IMAGE (view->priv->search_button),
&old_icon_name, &old_size);
if (button_icon_size != old_size)
{
GtkWidget *image;
icon_name = g_strdup (old_icon_name);
gtk_image_set_from_icon_name (GTK_IMAGE (view->priv->search_button),
icon_name, button_icon_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_icon_size);
g_free (icon_name);
}
if (GIMP_IS_LAYER_TREE_VIEW (view))
{
gtk_image_get_icon_name (GTK_IMAGE (view->priv->effect_header_image),
@ -1278,6 +1384,8 @@ static void
gimp_item_tree_view_real_set_image (GimpItemTreeView *view,
GimpImage *image)
{
GimpItemTreeViewClass *item_view_class;
if (view->priv->image == image)
return;
@ -1295,6 +1403,13 @@ gimp_item_tree_view_real_set_image (GimpItemTreeView *view,
g_signal_handlers_disconnect_by_func (view->priv->image,
gimp_item_tree_view_image_flush,
view);
g_signal_handlers_disconnect_by_func (view->priv->image,
G_CALLBACK (gimp_item_tree_view_item_links_changed),
view);
g_signal_handlers_disconnect_by_func (view->priv->image,
G_CALLBACK (gimp_item_tree_view_floating_selection_changed),
view);
}
view->priv->image = image;
@ -1323,8 +1438,24 @@ gimp_item_tree_view_real_set_image (GimpItemTreeView *view,
G_CALLBACK (gimp_item_tree_view_image_flush),
view);
g_signal_connect (view->priv->image,
"item-sets-changed",
G_CALLBACK (gimp_item_tree_view_item_links_changed),
view);
g_signal_connect (view->priv->image,
"floating-selection-changed",
G_CALLBACK (gimp_item_tree_view_floating_selection_changed),
view);
gimp_item_tree_view_item_changed (view->priv->image, view);
}
/* Call this even with no image, allowing to empty the link list. */
item_view_class = GIMP_ITEM_TREE_VIEW_GET_CLASS (view);
gimp_item_tree_view_item_links_changed (view->priv->image,
item_view_class->item_type,
view);
gimp_item_tree_view_floating_selection_changed (view->priv->image, view);
}
static void
@ -1581,6 +1712,22 @@ gimp_item_tree_view_select_items (GimpContainerView *view,
if (tree_view->priv->options_box)
gtk_widget_set_sensitive (tree_view->priv->options_box, options_sensitive);
if (g_list_length (items) > 1)
{
gchar *str;
str = g_strdup_printf (ngettext ("%d item selected", "%d items selected",
g_list_length (items)),
g_list_length (items));
gtk_label_set_text (GTK_LABEL (tree_view->priv->multi_selection_label), str);
g_free (str);
gtk_widget_show (tree_view->priv->multi_selection_label);
}
else
{
gtk_widget_hide (tree_view->priv->multi_selection_label);
}
return success;
}
@ -3086,3 +3233,475 @@ gimp_item_tree_effects_set_sensitive (GimpItemTreeView *view,
gtk_widget_set_sensitive (view->priv->effects_merge_button, is_sensitive);
gtk_widget_set_sensitive (view->priv->effects_remove_button, is_sensitive);
}
/* Functions for the item search/selection feature. */
static void
gimp_item_tree_view_create_search_popover (GimpItemTreeView *view,
GtkIconSize button_size)
{
PangoAttrList *attrs;
GtkWidget *grid;
GtkWidget *placeholder;
GtkWidget *button;
g_return_if_fail (GIMP_IS_ITEM_TREE_VIEW (view));
g_return_if_fail (view->priv->search_popover == NULL);
g_return_if_fail (GTK_IS_IMAGE (view->priv->search_button));
view->priv->search_popover = gtk_popover_new (view->priv->search_button);
gtk_popover_set_modal (GTK_POPOVER (view->priv->search_popover), TRUE);
g_signal_connect (view->priv->search_popover,
"show",
G_CALLBACK (gimp_item_tree_view_search_popover_shown),
view);
grid = gtk_grid_new ();
/* Link popover: regexp search. */
view->priv->link_search_entry = gtk_entry_new ();
gtk_entry_set_icon_from_icon_name (GTK_ENTRY (view->priv->link_search_entry),
GTK_ENTRY_ICON_SECONDARY,
"system-search");
gtk_grid_attach (GTK_GRID (grid),
view->priv->link_search_entry,
0, 0, 2, 1);
gtk_widget_show (view->priv->link_search_entry);
g_signal_connect (view->priv->link_search_entry,
"key-release-event",
G_CALLBACK (gimp_item_tree_view_search_key_release),
view);
g_signal_connect (GIMP_CONTAINER_TREE_VIEW (view)->view,
"start-interactive-search",
G_CALLBACK (gimp_item_tree_view_start_interactive_search),
view);
/* Link popover: existing links. */
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 (view->priv->link_list),
placeholder);
gtk_widget_show (placeholder);
gtk_grid_attach (GTK_GRID (grid),
view->priv->link_list,
0, 1, 2, 1);
gtk_widget_show (view->priv->link_list);
gtk_list_box_set_activate_on_single_click (GTK_LIST_BOX (view->priv->link_list),
TRUE);
/* Link popover: new links. */
view->priv->link_entry = gtk_entry_new ();
gtk_entry_set_placeholder_text (GTK_ENTRY (view->priv->link_entry),
_("New layer set's name"));
gtk_grid_attach (GTK_GRID (grid),
view->priv->link_entry,
0, 2, 1, 1);
gtk_widget_show (view->priv->link_entry);
button = gtk_button_new_from_icon_name (GIMP_ICON_LIST_ADD, button_size);
gtk_grid_attach (GTK_GRID (grid),
button,
1, 2, 1, 1);
g_signal_connect_swapped (button,
"clicked",
G_CALLBACK (gimp_item_tree_view_new_link_clicked),
view);
gtk_widget_show (button);
view->priv->new_link_button = button;
/* Enter on any entry activates the link creation then exits in case
* of success.
*/
g_signal_connect_swapped (view->priv->link_entry,
"activate",
G_CALLBACK (gimp_item_tree_view_new_link_exit),
view);
gtk_container_add (GTK_CONTAINER (view->priv->search_popover), grid);
gtk_widget_show (grid);
}
static void
gimp_item_tree_view_search_popover_shown (GtkPopover *popover,
GimpItemTreeView *view)
{
GimpImage *image;
GimpSelectMethod pattern_syntax;
image = gimp_item_tree_view_get_image (GIMP_ITEM_TREE_VIEW (view));
gtk_widget_set_sensitive (view->priv->link_entry, FALSE);
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;
}
gtk_widget_set_sensitive (view->priv->link_entry, TRUE);
gtk_widget_grab_focus (view->priv->link_search_entry);
g_object_get (image->gimp->config,
"items-select-method", &pattern_syntax,
NULL);
switch (pattern_syntax)
{
case GIMP_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 GIMP_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 GIMP_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 void
gimp_item_tree_view_floating_selection_changed (GimpImage *image,
GimpItemTreeView *view)
{
gtk_widget_set_sensitive (view->priv->search_button,
image && ! gimp_image_get_floating_selection (image));
}
static void
gimp_item_tree_view_item_links_changed (GimpImage *image,
GType item_type,
GimpItemTreeView *view)
{
GimpItemTreeViewClass *item_view_class;
GtkWidget *grid;
GtkWidget *label;
GtkWidget *event_box;
GtkWidget *icon;
GtkSizeGroup *label_size;
GList *links;
GList *iter;
item_view_class = GIMP_ITEM_TREE_VIEW_GET_CLASS (view);
if (item_type != item_view_class->item_type)
return;
gtk_container_foreach (GTK_CONTAINER (view->priv->link_list),
(GtkCallback) gtk_widget_destroy, NULL);
gtk_widget_set_sensitive (view->priv->search_button, image != NULL);
if (! image)
return;
links = gimp_image_get_stored_item_sets (image, item_type);
label_size = gtk_size_group_new (GTK_SIZE_GROUP_BOTH);
for (iter = links; iter; iter = iter->next)
{
GimpSelectMethod method;
grid = gtk_grid_new ();
label = gtk_label_new (gimp_object_get_name (iter->data));
gtk_label_set_xalign (GTK_LABEL (label), 0.0);
if (gimp_item_list_is_pattern (iter->data, &method))
{
gchar *display_name;
PangoAttrList *attrs;
display_name = g_strdup_printf ("<small>[%s]</small> %s",
method == GIMP_SELECT_PLAIN_TEXT ? _("search") :
(method == GIMP_SELECT_GLOB_PATTERN ? _("glob") : _("regexp")),
gimp_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 (gimp_item_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 (GIMP_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 (gimp_item_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
gimp_item_tree_view_link_clicked (GtkWidget *box,
GdkEvent *event,
GimpItemTreeView *view)
{
GimpImage *image;
GdkEventButton *bevent = (GdkEventButton *) event;
GdkModifierType modifiers;
image = gimp_item_tree_view_get_image (view);
g_return_val_if_fail (GIMP_IS_IMAGE (image), FALSE);
g_return_val_if_fail (GTK_IS_EVENT_BOX (box), FALSE);
modifiers = bevent->state & gimp_get_all_modifiers_mask ();
if (modifiers == GDK_SHIFT_MASK)
gimp_image_add_item_set (image, g_object_get_data (G_OBJECT (box), "link-set"));
else if (modifiers == GDK_CONTROL_MASK)
gimp_image_remove_item_set (image, g_object_get_data (G_OBJECT (box), "link-set"));
else if (modifiers == (GDK_CONTROL_MASK | GDK_SHIFT_MASK))
gimp_image_intersect_item_set (image, g_object_get_data (G_OBJECT (box), "link-set"));
else
gimp_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 gboolean
gimp_item_tree_view_unlink_clicked (GtkWidget *widget,
GdkEvent *event,
GimpItemTreeView *view)
{
GimpImage *image;
image = gimp_item_tree_view_get_image (view);
g_return_val_if_fail (GIMP_IS_IMAGE (image), FALSE);
gimp_image_unlink_item_set (image, g_object_get_data (G_OBJECT (widget), "link-set"));
return TRUE;
}
static gboolean
gimp_item_tree_view_search_key_release (GtkWidget *widget,
GdkEventKey *event,
GimpItemTreeView *view)
{
GimpItemTreeViewClass *item_view_class = GIMP_ITEM_TREE_VIEW_GET_CLASS (view);
GimpImage *image;
const gchar *pattern;
GimpSelectMethod 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 (gimp_item_tree_view_new_link_clicked (view))
gtk_widget_hide (view->priv->search_popover);
}
else
{
gtk_widget_hide (view->priv->search_popover);
}
return TRUE;
}
gtk_entry_set_attributes (GTK_ENTRY (view->priv->link_search_entry),
NULL);
image = gimp_item_tree_view_get_image (GIMP_ITEM_TREE_VIEW (view));
g_clear_object (&view->priv->link_pattern_set);
if (! image)
return TRUE;
g_object_get (image->gimp->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 = gimp_item_list_pattern_new (image,
item_view_class->item_type,
pattern_syntax,
pattern);
items = gimp_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);
gimp_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. */
gimp_image_set_selected_layers (image, NULL);
gimp_widget_blink (view->priv->link_search_entry);
}
else
{
gimp_image_set_selected_layers (image, items);
g_list_free (items);
}
}
else
{
gtk_widget_set_sensitive (view->priv->link_entry, TRUE);
}
return TRUE;
}
static gboolean
gimp_item_tree_view_start_interactive_search (GtkTreeView *tree_view,
GimpItemTreeView *layer_view)
{
gtk_widget_show (layer_view->priv->search_popover);
gtk_widget_grab_focus (layer_view->priv->link_search_entry);
return FALSE;
}
static gboolean
gimp_item_tree_view_new_link_clicked (GimpItemTreeView *view)
{
GimpItemTreeViewClass *item_view_class = GIMP_ITEM_TREE_VIEW_GET_CLASS (view);
GimpImage *image;
const gchar *name;
image = gimp_item_tree_view_get_image (GIMP_ITEM_TREE_VIEW (view));
if (! image)
return TRUE;
name = gtk_entry_get_text (GTK_ENTRY (view->priv->link_entry));
if (name && strlen (name) > 0)
{
GimpItemList *set;
set = gimp_item_list_named_new (image,
item_view_class->item_type,
name, NULL);
if (set)
{
gimp_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)
{
gimp_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
{
gimp_widget_blink (view->priv->link_entry);
gimp_widget_blink (view->priv->link_search_entry);
return FALSE;
}
return TRUE;
}
static void
gimp_item_tree_view_new_link_exit (GimpItemTreeView *view)
{
if (gimp_item_tree_view_new_link_clicked (view))
gtk_widget_hide (view->priv->search_popover);
}
static gboolean
gimp_item_tree_view_search_clicked (GtkWidget *main_column_button,
GdkEventButton *event,
GimpItemTreeView *view)
{
gtk_popover_popup (GTK_POPOVER (view->priv->search_popover));
return TRUE;
}

View File

@ -77,14 +77,6 @@ struct _GimpLayerTreeViewPrivate
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;
GimpItemList *link_pattern_set;
gint model_column_mask;
gint model_column_mask_visible;
@ -105,8 +97,6 @@ static void gimp_layer_tree_view_view_iface_init (GimpContainer
static void gimp_layer_tree_view_constructed (GObject *object);
static void gimp_layer_tree_view_finalize (GObject *object);
static void gimp_layer_tree_view_style_updated (GtkWidget *widget);
static void gimp_layer_tree_view_set_container (GimpContainerView *view,
GimpContainer *container);
static void gimp_layer_tree_view_set_context (GimpContainerView *view,
@ -150,26 +140,6 @@ static GimpItem * gimp_layer_tree_view_item_new (GimpImage
static void gimp_layer_tree_view_floating_selection_changed (GimpImage *image,
GimpLayerTreeView *view);
static void gimp_layer_tree_view_layer_links_changed (GimpImage *image,
GimpLayerTreeView *view);
static gboolean gimp_layer_tree_view_link_clicked (GtkWidget *box,
GdkEvent *event,
GimpLayerTreeView *view);
static void gimp_layer_tree_view_link_popover_shown (GtkPopover *popover,
GimpLayerTreeView *view);
static gboolean gimp_layer_tree_view_search_key_release (GtkWidget *widget,
GdkEventKey *event,
GimpLayerTreeView *view);
static gboolean gimp_layer_tree_view_start_interactive_search (GtkTreeView *tree_view,
GimpLayerTreeView *layer_view);
static void gimp_layer_tree_view_new_link_exit (GimpLayerTreeView *view);
static gboolean gimp_layer_tree_view_new_link_clicked (GimpLayerTreeView *view);
static gboolean gimp_layer_tree_view_unlink_clicked (GtkWidget *widget,
GdkEvent *event,
GimpLayerTreeView *view);
static void gimp_layer_tree_view_layer_mode_box_callback (GtkWidget *widget,
const GParamSpec *pspec,
GimpLayerTreeView *view);
@ -225,17 +195,13 @@ gimp_layer_tree_view_class_init (GimpLayerTreeViewClass *klass)
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GimpContainerTreeViewClass *tree_view_class;
GimpItemTreeViewClass *item_view_class;
GtkWidgetClass *widget_class;
widget_class = GTK_WIDGET_CLASS (klass);
tree_view_class = GIMP_CONTAINER_TREE_VIEW_CLASS (klass);
item_view_class = GIMP_ITEM_TREE_VIEW_CLASS (klass);
object_class->constructed = gimp_layer_tree_view_constructed;
object_class->finalize = gimp_layer_tree_view_finalize;
widget_class->style_updated = gimp_layer_tree_view_style_updated;
tree_view_class->drop_possible = gimp_layer_tree_view_drop_possible;
tree_view_class->drop_color = gimp_layer_tree_view_drop_color;
tree_view_class->drop_uri_list = gimp_layer_tree_view_drop_uri_list;
@ -290,8 +256,6 @@ gimp_layer_tree_view_init (GimpLayerTreeView *view)
view->priv = gimp_layer_tree_view_get_instance_private (view);
view->priv->link_pattern_set = NULL;
view->priv->model_column_mask =
gimp_container_tree_store_columns_add (tree_view->model_columns,
&tree_view->n_model_columns,
@ -351,10 +315,6 @@ gimp_layer_tree_view_constructed (GObject *object)
GimpContainerTreeView *tree_view = GIMP_CONTAINER_TREE_VIEW (object);
GimpLayerTreeView *layer_view = GIMP_LAYER_TREE_VIEW (object);
GtkWidget *button;
GtkWidget *placeholder;
GtkWidget *grid;
PangoAttrList *attrs;
GtkIconSize button_size;
G_OBJECT_CLASS (parent_class)->constructed (object);
@ -438,111 +398,6 @@ gimp_layer_tree_view_constructed (GObject *object)
gtk_box_reorder_child (gimp_editor_get_button_box (GIMP_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 (GIMP_ICON_LINKED,
button_size));
gtk_box_pack_start (gimp_editor_get_button_box (GIMP_EDITOR (layer_view)),
layer_view->priv->link_button, TRUE, TRUE, 0);
gtk_widget_show (layer_view->priv->link_button);
gimp_container_view_enable_dnd (GIMP_CONTAINER_VIEW (layer_view),
GTK_BUTTON (layer_view->priv->link_button),
GIMP_TYPE_LAYER);
gtk_box_reorder_child (gimp_editor_get_button_box (GIMP_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 (gimp_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 (gimp_layer_tree_view_search_key_release),
layer_view);
g_signal_connect (GIMP_CONTAINER_TREE_VIEW (layer_view)->view,
"start-interactive-search",
G_CALLBACK (gimp_layer_tree_view_start_interactive_search),
layer_view);
gtk_tree_view_set_search_entry (GIMP_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 (GIMP_ICON_LIST_ADD, button_size);
gtk_grid_attach (GTK_GRID (grid),
button,
1, 2, 1, 1);
g_signal_connect_swapped (button,
"clicked",
G_CALLBACK (gimp_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 (gimp_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 */
gimp_item_tree_view_add_lock (GIMP_ITEM_TREE_VIEW (tree_view),
@ -582,50 +437,6 @@ gimp_layer_tree_view_finalize (GObject *object)
}
/* GimpWidget methods */
static void
gimp_layer_tree_view_style_updated (GtkWidget *widget)
{
GimpLayerTreeView *view = GIMP_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);
}
}
/* GimpContainerView methods */
static void
@ -713,35 +524,9 @@ gimp_layer_tree_view_set_context (GimpContainerView *view,
{
GimpContainerTreeView *tree_view = GIMP_CONTAINER_TREE_VIEW (view);
GimpLayerTreeView *layer_view = GIMP_LAYER_TREE_VIEW (view);
GimpContext *old_context;
old_context = gimp_container_view_get_context (view);
if (old_context)
{
g_signal_handlers_disconnect_by_func (old_context->gimp->config,
G_CALLBACK (gimp_layer_tree_view_style_updated),
view);
}
parent_view_iface->set_context (view, context);
if (context)
{
g_signal_connect_object (context->gimp->config,
"notify::theme",
G_CALLBACK (gimp_layer_tree_view_style_updated),
view, G_CONNECT_AFTER | G_CONNECT_SWAPPED);
g_signal_connect_object (context->gimp->config,
"notify::override-theme-icon-size",
G_CALLBACK (gimp_layer_tree_view_style_updated),
view, G_CONNECT_AFTER | G_CONNECT_SWAPPED);
g_signal_connect_object (context->gimp->config,
"notify::custom-icon-size",
G_CALLBACK (gimp_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,
@ -1116,9 +901,6 @@ gimp_layer_tree_view_set_image (GimpItemTreeView *view,
g_signal_handlers_disconnect_by_func (gimp_item_tree_view_get_image (view),
gimp_layer_tree_view_floating_selection_changed,
view);
g_signal_handlers_disconnect_by_func (gimp_item_tree_view_get_image (view),
G_CALLBACK (gimp_layer_tree_view_layer_links_changed),
view);
}
GIMP_ITEM_TREE_VIEW_CLASS (parent_class)->set_image (view, image);
@ -1129,10 +911,6 @@ gimp_layer_tree_view_set_image (GimpItemTreeView *view,
"floating-selection-changed",
G_CALLBACK (gimp_layer_tree_view_floating_selection_changed),
view);
g_signal_connect (gimp_item_tree_view_get_image (view),
"layer-sets-changed",
G_CALLBACK (gimp_layer_tree_view_layer_links_changed),
view);
/* call gimp_layer_tree_view_floating_selection_changed() now, to update
* the floating selection's row attributes.
@ -1141,9 +919,6 @@ gimp_layer_tree_view_set_image (GimpItemTreeView *view,
gimp_item_tree_view_get_image (view),
layer_view);
}
/* Call this even with no image, allowing to empty the link list. */
gimp_layer_tree_view_layer_links_changed (gimp_item_tree_view_get_image (view),
layer_view);
gimp_layer_tree_view_update_highlight (layer_view);
}
@ -1220,356 +995,9 @@ gimp_layer_tree_view_floating_selection_changed (GimpImage *image,
g_list_free (all_layers);
}
gtk_widget_set_sensitive (layer_view->priv->link_button, ! floating_sel);
gimp_layer_tree_view_update_highlight (layer_view);
}
static void
gimp_layer_tree_view_layer_links_changed (GimpImage *image,
GimpLayerTreeView *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 = gimp_image_get_stored_item_sets (image, GIMP_TYPE_LAYER);
label_size = gtk_size_group_new (GTK_SIZE_GROUP_BOTH);
for (iter = links; iter; iter = iter->next)
{
GimpSelectMethod method;
grid = gtk_grid_new ();
label = gtk_label_new (gimp_object_get_name (iter->data));
gtk_label_set_xalign (GTK_LABEL (label), 0.0);
if (gimp_item_list_is_pattern (iter->data, &method))
{
gchar *display_name;
PangoAttrList *attrs;
display_name = g_strdup_printf ("<small>[%s]</small> %s",
method == GIMP_SELECT_PLAIN_TEXT ? _("search") :
(method == GIMP_SELECT_GLOB_PATTERN ? _("glob") : _("regexp")),
gimp_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 (gimp_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 (GIMP_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 (gimp_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
gimp_layer_tree_view_link_clicked (GtkWidget *box,
GdkEvent *event,
GimpLayerTreeView *view)
{
GimpImage *image;
GdkEventButton *bevent = (GdkEventButton *) event;
GdkModifierType modifiers;
image = gimp_item_tree_view_get_image (GIMP_ITEM_TREE_VIEW (view));
g_return_val_if_fail (GIMP_IS_IMAGE (image), FALSE);
g_return_val_if_fail (GTK_IS_EVENT_BOX (box), FALSE);
modifiers = bevent->state & gimp_get_all_modifiers_mask ();
if (modifiers == GDK_SHIFT_MASK)
gimp_image_add_item_set (image, g_object_get_data (G_OBJECT (box), "link-set"));
else if (modifiers == GDK_CONTROL_MASK)
gimp_image_remove_item_set (image, g_object_get_data (G_OBJECT (box), "link-set"));
else if (modifiers == (GDK_CONTROL_MASK | GDK_SHIFT_MASK))
gimp_image_intersect_item_set (image, g_object_get_data (G_OBJECT (box), "link-set"));
else
gimp_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
gimp_layer_tree_view_link_popover_shown (GtkPopover *popover,
GimpLayerTreeView *view)
{
GimpImage *image;
GimpSelectMethod pattern_syntax;
image = gimp_item_tree_view_get_image (GIMP_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->gimp->config,
"items-select-method", &pattern_syntax,
NULL);
switch (pattern_syntax)
{
case GIMP_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 GIMP_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 GIMP_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
gimp_layer_tree_view_search_key_release (GtkWidget *widget,
GdkEventKey *event,
GimpLayerTreeView *view)
{
GimpImage *image;
const gchar *pattern;
GimpSelectMethod 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 (gimp_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 = gimp_item_tree_view_get_image (GIMP_ITEM_TREE_VIEW (view));
g_clear_object (&view->priv->link_pattern_set);
if (! image)
return TRUE;
g_object_get (image->gimp->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 = gimp_item_list_pattern_new (image,
GIMP_TYPE_LAYER,
pattern_syntax,
pattern);
items = gimp_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);
gimp_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. */
gimp_image_set_selected_layers (image, NULL);
gimp_widget_blink (view->priv->link_search_entry);
}
else
{
gimp_image_set_selected_layers (image, items);
g_list_free (items);
}
}
else
{
gtk_widget_set_sensitive (view->priv->link_entry, TRUE);
}
return TRUE;
}
static gboolean
gimp_layer_tree_view_start_interactive_search (GtkTreeView *tree_view,
GimpLayerTreeView *layer_view)
{
gtk_widget_show (layer_view->priv->link_popover);
gtk_widget_grab_focus (layer_view->priv->link_search_entry);
return FALSE;
}
static void
gimp_layer_tree_view_new_link_exit (GimpLayerTreeView *view)
{
if (gimp_layer_tree_view_new_link_clicked (view))
gtk_widget_hide (view->priv->link_popover);
}
static gboolean
gimp_layer_tree_view_new_link_clicked (GimpLayerTreeView *view)
{
GimpImage *image;
const gchar *name;
image = gimp_item_tree_view_get_image (GIMP_ITEM_TREE_VIEW (view));
if (! image)
return TRUE;
name = gtk_entry_get_text (GTK_ENTRY (view->priv->link_entry));
if (name && strlen (name) > 0)
{
GimpItemList *set;
set = gimp_item_list_named_new (image, GIMP_TYPE_LAYER, name, NULL);
if (set)
{
gimp_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)
{
gimp_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
{
gimp_widget_blink (view->priv->link_entry);
gimp_widget_blink (view->priv->link_search_entry);
return FALSE;
}
return TRUE;
}
static gboolean
gimp_layer_tree_view_unlink_clicked (GtkWidget *widget,
GdkEvent *event,
GimpLayerTreeView *view)
{
GimpImage *image;
image = gimp_item_tree_view_get_image (GIMP_ITEM_TREE_VIEW (view));
g_return_val_if_fail (GIMP_IS_IMAGE (image), FALSE);
gimp_image_unlink_item_set (image,
g_object_get_data (G_OBJECT (widget),
"link-set"));
return TRUE;
}
/* Paint Mode, Opacity and Lock alpha callbacks */