diff --git a/app/core/gimp-gui.c b/app/core/gimp-gui.c
index 3e6c4e5248..6ec2a513ee 100644
--- a/app/core/gimp-gui.c
+++ b/app/core/gimp-gui.c
@@ -419,6 +419,7 @@ gimp_pdb_dialog_new (Gimp *gimp,
g_return_val_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress), FALSE);
g_return_val_if_fail (g_type_is_a (contents_type, GIMP_TYPE_RESOURCE) ||
g_type_is_a (contents_type, GIMP_TYPE_DRAWABLE) ||
+ g_type_is_a (contents_type, GIMP_TYPE_ITEM) ||
g_type_is_a (contents_type, GIMP_TYPE_IMAGE), FALSE);
g_return_val_if_fail (object == NULL ||
g_type_is_a (G_TYPE_FROM_INSTANCE (object), contents_type), FALSE);
@@ -453,6 +454,7 @@ gimp_pdb_dialog_set (Gimp *gimp,
g_return_val_if_fail (GIMP_IS_GIMP (gimp), FALSE);
g_return_val_if_fail (g_type_is_a (contents_type, GIMP_TYPE_RESOURCE) ||
contents_type == GIMP_TYPE_DRAWABLE ||
+ contents_type == GIMP_TYPE_ITEM ||
contents_type == GIMP_TYPE_IMAGE, FALSE);
g_return_val_if_fail (callback_name != NULL, FALSE);
g_return_val_if_fail (object == NULL || g_type_is_a (G_TYPE_FROM_INSTANCE (object), contents_type), FALSE);
@@ -480,6 +482,7 @@ gimp_pdb_dialog_close (Gimp *gimp,
g_return_val_if_fail (GIMP_IS_GIMP (gimp), FALSE);
g_return_val_if_fail (g_type_is_a (contents_type, GIMP_TYPE_RESOURCE) ||
contents_type == GIMP_TYPE_DRAWABLE ||
+ contents_type == GIMP_TYPE_ITEM ||
contents_type == GIMP_TYPE_IMAGE, FALSE);
g_return_val_if_fail (callback_name != NULL, FALSE);
diff --git a/app/gui/gui-vtable.c b/app/gui/gui-vtable.c
index 090d1d48d4..e8ab408f20 100644
--- a/app/gui/gui-vtable.c
+++ b/app/gui/gui-vtable.c
@@ -86,6 +86,7 @@
#include "widgets/gimphelp-ids.h"
#include "widgets/gimpimageselect.h"
#include "widgets/gimpimageview.h"
+#include "widgets/gimpitemselect.h"
#include "widgets/gimpmenufactory.h"
#include "widgets/gimppaletteselect.h"
#include "widgets/gimppatternselect.h"
@@ -668,6 +669,11 @@ gui_pdb_dialog_new (Gimp *gimp,
dialog_type = GIMP_TYPE_PICKABLE_SELECT;
dialog_role = "gimp-pickable-selection";
}
+ else if (g_type_is_a (contents_type, GIMP_TYPE_ITEM))
+ {
+ dialog_type = GIMP_TYPE_ITEM_SELECT;
+ dialog_role = "gimp-item-selection";
+ }
else
{
g_return_val_if_reached (FALSE);
@@ -675,10 +681,10 @@ gui_pdb_dialog_new (Gimp *gimp,
if (dialog_type != G_TYPE_NONE)
{
- if (! object && ! g_type_is_a (contents_type, GIMP_TYPE_DRAWABLE))
+ if (! object && ! g_type_is_a (contents_type, GIMP_TYPE_ITEM))
object = gimp_context_get_by_type (context, contents_type);
- if (object || g_type_is_a (contents_type, GIMP_TYPE_DRAWABLE))
+ if (object || g_type_is_a (contents_type, GIMP_TYPE_ITEM))
{
gint n_properties = 0;
gchar **names = NULL;
@@ -794,6 +800,10 @@ gui_pdb_dialog_set (Gimp *gimp,
{
klass = g_type_class_peek (GIMP_TYPE_PICKABLE_SELECT);
}
+ else if (contents_type == GIMP_TYPE_ITEM)
+ {
+ klass = g_type_class_peek (GIMP_TYPE_ITEM_SELECT);
+ }
g_return_val_if_fail (klass != NULL, FALSE);
@@ -848,6 +858,8 @@ gui_pdb_dialog_close (Gimp *gimp,
klass = g_type_class_peek (GIMP_TYPE_PATTERN_SELECT);
else if (contents_type == GIMP_TYPE_DRAWABLE)
klass = g_type_class_peek (GIMP_TYPE_PICKABLE_SELECT);
+ else if (contents_type == GIMP_TYPE_ITEM)
+ klass = g_type_class_peek (GIMP_TYPE_ITEM_SELECT);
if (klass)
{
diff --git a/app/pdb/internal-procs.c b/app/pdb/internal-procs.c
index a6aa965fc4..f9d6a3319a 100644
--- a/app/pdb/internal-procs.c
+++ b/app/pdb/internal-procs.c
@@ -30,7 +30,7 @@
#include "internal-procs.h"
-/* 782 procedures registered total */
+/* 785 procedures registered total */
void
internal_procs_init (GimpPDB *pdb)
diff --git a/app/pdb/item-cmds.c b/app/pdb/item-cmds.c
index bac87e621e..2a32a0772f 100644
--- a/app/pdb/item-cmds.c
+++ b/app/pdb/item-cmds.c
@@ -31,6 +31,8 @@
#include "pdb-types.h"
+#include "core/gimp.h"
+#include "core/gimpdatafactory.h"
#include "core/gimpgrouplayer.h"
#include "core/gimpimage.h"
#include "core/gimpitem.h"
@@ -1113,6 +1115,95 @@ item_get_parasite_list_invoker (GimpProcedure *procedure,
return return_vals;
}
+static GimpValueArray *
+items_popup_invoker (GimpProcedure *procedure,
+ Gimp *gimp,
+ GimpContext *context,
+ GimpProgress *progress,
+ const GimpValueArray *args,
+ GError **error)
+{
+ gboolean success = TRUE;
+ const gchar *callback;
+ const gchar *popup_title;
+ const gchar *item_type;
+ GimpItem *initial_item;
+ GBytes *parent_window;
+
+ callback = g_value_get_string (gimp_value_array_index (args, 0));
+ popup_title = g_value_get_string (gimp_value_array_index (args, 1));
+ item_type = g_value_get_string (gimp_value_array_index (args, 2));
+ initial_item = g_value_get_object (gimp_value_array_index (args, 3));
+ parent_window = g_value_get_boxed (gimp_value_array_index (args, 4));
+
+ if (success)
+ {
+ if (gimp->no_interface ||
+ ! gimp_pdb_lookup_procedure (gimp->pdb, callback) ||
+ ! gimp_pdb_dialog_new (gimp, context, progress,
+ g_type_from_name (item_type),
+ parent_window, popup_title, callback,
+ GIMP_OBJECT (initial_item),
+ NULL))
+ success = FALSE;
+ }
+
+ return gimp_procedure_get_return_values (procedure, success,
+ error ? *error : NULL);
+}
+
+static GimpValueArray *
+items_close_popup_invoker (GimpProcedure *procedure,
+ Gimp *gimp,
+ GimpContext *context,
+ GimpProgress *progress,
+ const GimpValueArray *args,
+ GError **error)
+{
+ gboolean success = TRUE;
+ const gchar *callback;
+
+ callback = g_value_get_string (gimp_value_array_index (args, 0));
+
+ if (success)
+ {
+ if (gimp->no_interface ||
+ ! gimp_pdb_lookup_procedure (gimp->pdb, callback) ||
+ ! gimp_pdb_dialog_close (gimp, GIMP_TYPE_ITEM, callback))
+ success = FALSE;
+ }
+
+ return gimp_procedure_get_return_values (procedure, success,
+ error ? *error : NULL);
+}
+
+static GimpValueArray *
+items_set_popup_invoker (GimpProcedure *procedure,
+ Gimp *gimp,
+ GimpContext *context,
+ GimpProgress *progress,
+ const GimpValueArray *args,
+ GError **error)
+{
+ gboolean success = TRUE;
+ const gchar *callback;
+ GimpItem *item;
+
+ callback = g_value_get_string (gimp_value_array_index (args, 0));
+ item = g_value_get_object (gimp_value_array_index (args, 1));
+
+ if (success)
+ {
+ if (gimp->no_interface ||
+ ! gimp_pdb_lookup_procedure (gimp->pdb, callback) ||
+ ! gimp_pdb_dialog_set (gimp, GIMP_TYPE_ITEM, callback, GIMP_OBJECT (item), NULL))
+ success = FALSE;
+ }
+
+ return gimp_procedure_get_return_values (procedure, success,
+ error ? *error : NULL);
+}
+
void
register_item_procs (GimpPDB *pdb)
{
@@ -2201,4 +2292,108 @@ register_item_procs (GimpPDB *pdb)
GIMP_PARAM_READWRITE));
gimp_pdb_register_procedure (pdb, procedure);
g_object_unref (procedure);
+
+ /*
+ * gimp-items-popup
+ */
+ procedure = gimp_procedure_new (items_popup_invoker, FALSE);
+ gimp_object_set_static_name (GIMP_OBJECT (procedure),
+ "gimp-items-popup");
+ gimp_procedure_set_static_help (procedure,
+ "Invokes the item selection dialog.",
+ "Opens a dialog letting a user choose an item .",
+ NULL);
+ gimp_procedure_set_static_attribution (procedure,
+ "Alex S.",
+ "Alex S.",
+ "2025");
+ gimp_procedure_add_argument (procedure,
+ gimp_param_spec_string ("callback",
+ "callback",
+ "The callback PDB proc to call when user chooses an item",
+ FALSE, FALSE, TRUE,
+ NULL,
+ GIMP_PARAM_READWRITE));
+ gimp_procedure_add_argument (procedure,
+ gimp_param_spec_string ("popup-title",
+ "popup title",
+ "Title of the item selection dialog",
+ FALSE, FALSE, FALSE,
+ NULL,
+ GIMP_PARAM_READWRITE));
+ gimp_procedure_add_argument (procedure,
+ gimp_param_spec_string ("item-type",
+ "item type",
+ "The name of the GIMP_TYPE_ITEM subtype",
+ FALSE, FALSE, TRUE,
+ NULL,
+ GIMP_PARAM_READWRITE));
+ gimp_procedure_add_argument (procedure,
+ gimp_param_spec_item ("initial-item",
+ "initial item",
+ "The item to set as the initial choice",
+ TRUE,
+ GIMP_PARAM_READWRITE | GIMP_PARAM_NO_VALIDATE));
+ gimp_procedure_add_argument (procedure,
+ g_param_spec_boxed ("parent-window",
+ "parent window",
+ "An optional parent window handle for the popup to be set transient to",
+ G_TYPE_BYTES,
+ GIMP_PARAM_READWRITE));
+ gimp_pdb_register_procedure (pdb, procedure);
+ g_object_unref (procedure);
+
+ /*
+ * gimp-items-close-popup
+ */
+ procedure = gimp_procedure_new (items_close_popup_invoker, FALSE);
+ gimp_object_set_static_name (GIMP_OBJECT (procedure),
+ "gimp-items-close-popup");
+ gimp_procedure_set_static_help (procedure,
+ "Close the item selection dialog.",
+ "Closes an open item selection dialog.",
+ NULL);
+ gimp_procedure_set_static_attribution (procedure,
+ "Alex S.",
+ "Alex S.",
+ "2025");
+ gimp_procedure_add_argument (procedure,
+ gimp_param_spec_string ("callback",
+ "callback",
+ "The name of the callback registered for this pop-up",
+ FALSE, FALSE, TRUE,
+ NULL,
+ GIMP_PARAM_READWRITE));
+ gimp_pdb_register_procedure (pdb, procedure);
+ g_object_unref (procedure);
+
+ /*
+ * gimp-items-set-popup
+ */
+ procedure = gimp_procedure_new (items_set_popup_invoker, FALSE);
+ gimp_object_set_static_name (GIMP_OBJECT (procedure),
+ "gimp-items-set-popup");
+ gimp_procedure_set_static_help (procedure,
+ "Sets the selected item in a item selection dialog.",
+ "Sets the selected item in a item selection dialog.",
+ NULL);
+ gimp_procedure_set_static_attribution (procedure,
+ "Alex S.",
+ "Alex S.",
+ "2025");
+ gimp_procedure_add_argument (procedure,
+ gimp_param_spec_string ("callback",
+ "callback",
+ "The name of the callback registered for this pop-up",
+ FALSE, FALSE, TRUE,
+ NULL,
+ GIMP_PARAM_READWRITE));
+ gimp_procedure_add_argument (procedure,
+ gimp_param_spec_item ("item",
+ "item",
+ "The item to set as selected",
+ FALSE,
+ GIMP_PARAM_READWRITE | GIMP_PARAM_NO_VALIDATE));
+ gimp_pdb_register_procedure (pdb, procedure);
+ g_object_unref (procedure);
}
diff --git a/app/widgets/gimpitemchooser.c b/app/widgets/gimpitemchooser.c
new file mode 100644
index 0000000000..cf8412c5ed
--- /dev/null
+++ b/app/widgets/gimpitemchooser.c
@@ -0,0 +1,639 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpitemchooser.c
+ *
+ * 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 .
+ */
+
+#include "config.h"
+
+#include
+#include
+#include
+
+#include "libgimpbase/gimpbase.h"
+#include "libgimpwidgets/gimpwidgets.h"
+
+#include "widgets-types.h"
+
+#include "core/gimp.h"
+#include "core/gimpchannel.h"
+#include "core/gimpcontext.h"
+#include "core/gimpimage.h"
+#include "core/gimpitem.h"
+#include "core/gimplayer.h"
+#include "core/gimpviewable.h"
+
+#include "path/gimppath.h"
+
+#include "gimpcontainertreeview.h"
+#include "gimpcontainerview.h"
+#include "gimpitemchooser.h"
+#include "gimpviewrenderer.h"
+
+#include "gimp-intl.h"
+
+
+enum
+{
+ ACTIVATE,
+ LAST_SIGNAL
+};
+
+enum
+{
+ PROP_0,
+ PROP_CONTEXT,
+ PROP_ITEM_TYPE,
+ PROP_ITEM,
+ PROP_VIEW_SIZE,
+ PROP_VIEW_BORDER_WIDTH
+};
+
+struct _GimpItemChooserPrivate
+{
+ GType item_type;
+ GimpItem *item;
+ GimpContext *context;
+
+ gint view_size;
+ gint view_border_width;
+
+ GtkWidget *image_view;
+ GtkWidget *layer_view;
+ GtkWidget *channel_view;
+ GtkWidget *path_view;
+ GtkWidget *layer_label;
+};
+
+
+static void gimp_item_chooser_constructed (GObject *object);
+static void gimp_item_chooser_finalize (GObject *object);
+static void gimp_item_chooser_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void gimp_item_chooser_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec);
+
+static void gimp_item_chooser_image_changed (GimpContext *context,
+ GimpImage *image,
+ GimpItemChooser *chooser);
+static void gimp_item_chooser_item_activated (GimpContainerView *view,
+ GimpItem *item,
+ GimpItemChooser *chooser);
+static void gimp_item_chooser_items_selected (GimpContainerView *view,
+ GimpItemChooser *chooser);
+
+
+G_DEFINE_TYPE_WITH_PRIVATE (GimpItemChooser, gimp_item_chooser, GTK_TYPE_FRAME)
+
+#define parent_class gimp_item_chooser_parent_class
+
+static guint signals[LAST_SIGNAL];
+
+static void
+gimp_item_chooser_class_init (GimpItemChooserClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->constructed = gimp_item_chooser_constructed;
+ object_class->finalize = gimp_item_chooser_finalize;
+ object_class->get_property = gimp_item_chooser_get_property;
+ object_class->set_property = gimp_item_chooser_set_property;
+
+ /**
+ * GimpItemChooser::activate:
+ * @chooser:
+ *
+ * Emitted when a item is activated, which is mostly forwarding when
+ * "item-activated" signal is emitted from any of either the image, layer or
+ * channel view. E.g. this happens when one double-click on one of the
+ * items.
+ */
+ signals[ACTIVATE] =
+ g_signal_new ("activate",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (GimpItemChooserClass, activate),
+ NULL, NULL, NULL,
+ G_TYPE_NONE, 1,
+ GIMP_TYPE_OBJECT);
+
+ g_object_class_install_property (object_class, PROP_CONTEXT,
+ g_param_spec_object ("context",
+ NULL, NULL,
+ GIMP_TYPE_CONTEXT,
+ GIMP_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY));
+
+ g_object_class_install_property (object_class, PROP_ITEM_TYPE,
+ g_param_spec_gtype ("item-type",
+ NULL, NULL,
+ GIMP_TYPE_ITEM,
+ GIMP_PARAM_WRITABLE |
+ G_PARAM_CONSTRUCT_ONLY));
+
+ g_object_class_install_property (object_class, PROP_ITEM,
+ g_param_spec_object ("item",
+ NULL, NULL,
+ GIMP_TYPE_ITEM,
+ GIMP_PARAM_READWRITE |
+ G_PARAM_EXPLICIT_NOTIFY));
+
+ g_object_class_install_property (object_class, PROP_VIEW_SIZE,
+ g_param_spec_int ("view-size",
+ NULL, NULL,
+ 1, GIMP_VIEWABLE_MAX_PREVIEW_SIZE,
+ GIMP_VIEW_SIZE_MEDIUM,
+ GIMP_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT));
+
+ g_object_class_install_property (object_class, PROP_VIEW_BORDER_WIDTH,
+ g_param_spec_int ("view-border-width",
+ NULL, NULL,
+ 0,
+ GIMP_VIEW_MAX_BORDER_WIDTH,
+ 1,
+ GIMP_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT));
+}
+
+static void
+gimp_item_chooser_init (GimpItemChooser *chooser)
+{
+ chooser->priv = gimp_item_chooser_get_instance_private (chooser);
+
+ chooser->priv->view_size = GIMP_VIEW_SIZE_SMALL;
+ chooser->priv->view_border_width = 1;
+
+ chooser->priv->layer_view = NULL;
+ chooser->priv->channel_view = NULL;
+}
+
+static void
+gimp_item_chooser_constructed (GObject *object)
+{
+ GimpItemChooser *chooser = GIMP_ITEM_CHOOSER (object);
+ GtkWidget *hbox;
+ GtkWidget *vbox;
+ GtkWidget *label;
+ GtkWidget *notebook;
+ GimpImage *image;
+
+ G_OBJECT_CLASS (parent_class)->constructed (object);
+
+ gimp_assert (GIMP_IS_CONTEXT (chooser->priv->context));
+
+ gtk_frame_set_shadow_type (GTK_FRAME (chooser), GTK_SHADOW_OUT);
+
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
+ gtk_container_set_border_width (GTK_CONTAINER (hbox), 6);
+ gtk_container_add (GTK_CONTAINER (chooser), hbox);
+ gtk_widget_set_visible (hbox, TRUE);
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 4);
+ gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, 0);
+ gtk_widget_set_visible (vbox, TRUE);
+
+ label = gtk_label_new (_("Images"));
+ gtk_label_set_xalign (GTK_LABEL (label), 0.0);
+ gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
+ gtk_widget_set_visible (label, TRUE);
+
+ chooser->priv->image_view =
+ gimp_container_tree_view_new (chooser->priv->context->gimp->images,
+ chooser->priv->context,
+ chooser->priv->view_size,
+ chooser->priv->view_border_width);
+ gimp_container_box_set_size_request (GIMP_CONTAINER_BOX (chooser->priv->image_view),
+ 4 * (chooser->priv->view_size +
+ 2 * chooser->priv->view_border_width),
+ 4 * (chooser->priv->view_size +
+ 2 * chooser->priv->view_border_width));
+ gtk_box_pack_start (GTK_BOX (vbox), chooser->priv->image_view, TRUE, TRUE, 0);
+ gtk_widget_set_visible (chooser->priv->image_view, TRUE);
+
+ g_signal_connect_object (chooser->priv->image_view, "item-activated",
+ G_CALLBACK (gimp_item_chooser_item_activated),
+ G_OBJECT (chooser), 0);
+ g_signal_connect_object (chooser->priv->image_view, "selection-changed",
+ G_CALLBACK (gimp_item_chooser_items_selected),
+ G_OBJECT (chooser), 0);
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 4);
+ gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, 0);
+ gtk_widget_set_visible (vbox, TRUE);
+
+ chooser->priv->layer_label = label =
+ gtk_label_new (_("Select an image in the left pane"));
+ gtk_label_set_xalign (GTK_LABEL (label), 0.0);
+ gtk_label_set_ellipsize (GTK_LABEL (label), PANGO_ELLIPSIZE_END);
+ gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
+ gtk_widget_set_visible (label, TRUE);
+
+ notebook = gtk_notebook_new ();
+ gtk_box_pack_start (GTK_BOX (vbox), notebook, TRUE, TRUE, 0);
+ gtk_widget_set_visible (notebook, TRUE);
+
+ if (g_type_is_a (GIMP_TYPE_LAYER, chooser->priv->item_type))
+ {
+ chooser->priv->layer_view =
+ gimp_container_tree_view_new (NULL,
+ chooser->priv->context,
+ chooser->priv->view_size,
+ chooser->priv->view_border_width);
+ gtk_tree_view_set_show_expanders (GTK_TREE_VIEW (GIMP_CONTAINER_TREE_VIEW (chooser->priv->layer_view)->view),
+ TRUE);
+ gimp_container_box_set_size_request (GIMP_CONTAINER_BOX (chooser->priv->layer_view),
+ 4 * (chooser->priv->view_size +
+ 2 * chooser->priv->view_border_width),
+ 4 * (chooser->priv->view_size +
+ 2 * chooser->priv->view_border_width));
+ gtk_notebook_append_page (GTK_NOTEBOOK (notebook),
+ chooser->priv->layer_view,
+ gtk_label_new (_("Layers")));
+ gtk_widget_set_visible (chooser->priv->layer_view, TRUE);
+
+ g_signal_connect_object (chooser->priv->layer_view, "item-activated",
+ G_CALLBACK (gimp_item_chooser_item_activated),
+ G_OBJECT (chooser), 0);
+ g_signal_connect_object (chooser->priv->layer_view, "selection-changed",
+ G_CALLBACK (gimp_item_chooser_items_selected),
+ G_OBJECT (chooser), 0);
+ }
+
+ if (g_type_is_a (GIMP_TYPE_CHANNEL, chooser->priv->item_type))
+ {
+ chooser->priv->channel_view =
+ gimp_container_tree_view_new (NULL,
+ chooser->priv->context,
+ chooser->priv->view_size,
+ chooser->priv->view_border_width);
+ gimp_container_box_set_size_request (GIMP_CONTAINER_BOX (chooser->priv->channel_view),
+ 4 * (chooser->priv->view_size +
+ 2 * chooser->priv->view_border_width),
+ 4 * (chooser->priv->view_size +
+ 2 * chooser->priv->view_border_width));
+ gtk_notebook_append_page (GTK_NOTEBOOK (notebook),
+ chooser->priv->channel_view,
+ gtk_label_new (_("Channels")));
+ gtk_widget_set_visible (chooser->priv->channel_view, TRUE);
+
+ g_signal_connect_object (chooser->priv->channel_view, "item-activated",
+ G_CALLBACK (gimp_item_chooser_item_activated),
+ G_OBJECT (chooser), 0);
+ g_signal_connect_object (chooser->priv->channel_view, "selection-changed",
+ G_CALLBACK (gimp_item_chooser_items_selected),
+ G_OBJECT (chooser), 0);
+ }
+
+ if (g_type_is_a (GIMP_TYPE_PATH, chooser->priv->item_type))
+ {
+ chooser->priv->path_view =
+ gimp_container_tree_view_new (NULL,
+ chooser->priv->context,
+ chooser->priv->view_size,
+ chooser->priv->view_border_width);
+ gimp_container_box_set_size_request (GIMP_CONTAINER_BOX (chooser->priv->path_view),
+ 4 * (chooser->priv->view_size +
+ 2 * chooser->priv->view_border_width),
+ 4 * (chooser->priv->view_size +
+ 2 * chooser->priv->view_border_width));
+ gtk_notebook_append_page (GTK_NOTEBOOK (notebook),
+ chooser->priv->path_view,
+ gtk_label_new (_("Paths")));
+ gtk_widget_set_visible (chooser->priv->path_view, TRUE);
+
+ g_signal_connect_object (chooser->priv->path_view, "item-activated",
+ G_CALLBACK (gimp_item_chooser_item_activated),
+ G_OBJECT (chooser), 0);
+ g_signal_connect_object (chooser->priv->path_view, "selection-changed",
+ G_CALLBACK (gimp_item_chooser_items_selected),
+ G_OBJECT (chooser), 0);
+ }
+
+ g_signal_connect_object (chooser->priv->context, "image-changed",
+ G_CALLBACK (gimp_item_chooser_image_changed),
+ G_OBJECT (chooser), 0);
+
+ image = gimp_context_get_image (chooser->priv->context);
+ gimp_item_chooser_image_changed (chooser->priv->context, image, chooser);
+}
+
+static void
+gimp_item_chooser_finalize (GObject *object)
+{
+ GimpItemChooser *chooser = GIMP_ITEM_CHOOSER (object);
+
+ g_clear_object (&chooser->priv->item);
+ g_clear_object (&chooser->priv->context);
+
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+gimp_item_chooser_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GimpItemChooser *chooser = GIMP_ITEM_CHOOSER (object);
+
+ switch (property_id)
+ {
+ case PROP_CONTEXT:
+ chooser->priv->context = g_value_dup_object (value);
+ break;
+
+ case PROP_VIEW_SIZE:
+ chooser->priv->view_size = g_value_get_int (value);
+ break;
+
+ case PROP_VIEW_BORDER_WIDTH:
+ chooser->priv->view_border_width = g_value_get_int (value);
+ break;
+
+ case PROP_ITEM_TYPE:
+ g_return_if_fail (g_value_get_gtype (value) == GIMP_TYPE_LAYER ||
+ g_value_get_gtype (value) == GIMP_TYPE_CHANNEL ||
+ g_value_get_gtype (value) == GIMP_TYPE_DRAWABLE ||
+ g_value_get_gtype (value) == GIMP_TYPE_PATH ||
+ g_value_get_gtype (value) == GIMP_TYPE_IMAGE ||
+ g_value_get_gtype (value) == GIMP_TYPE_ITEM);
+ chooser->priv->item_type = g_value_get_gtype (value);
+ break;
+
+ case PROP_ITEM:
+ g_return_if_fail (g_value_get_object (value) == NULL ||
+ g_type_is_a (G_TYPE_FROM_INSTANCE (g_value_get_object (value)),
+ chooser->priv->item_type));
+ gimp_item_chooser_set_item (chooser, g_value_get_object (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gimp_item_chooser_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GimpItemChooser *chooser = GIMP_ITEM_CHOOSER (object);
+
+ switch (property_id)
+ {
+ case PROP_CONTEXT:
+ g_value_set_object (value, chooser->priv->context);
+ break;
+
+ case PROP_ITEM_TYPE:
+ g_value_set_gtype (value, chooser->priv->item_type);
+ break;
+
+ case PROP_ITEM:
+ g_value_set_object (value, chooser->priv->item);
+ break;
+
+ case PROP_VIEW_SIZE:
+ g_value_set_int (value, chooser->priv->view_size);
+ break;
+
+ case PROP_VIEW_BORDER_WIDTH:
+ g_value_set_int (value, chooser->priv->view_border_width);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+GtkWidget *
+gimp_item_chooser_new (GimpContext *context,
+ GType item_type,
+ gint view_size,
+ gint view_border_width)
+{
+ g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL);
+ g_return_val_if_fail (view_size > 0 &&
+ view_size <= GIMP_VIEWABLE_MAX_POPUP_SIZE, NULL);
+ g_return_val_if_fail (view_border_width >= 0 &&
+ view_border_width <= GIMP_VIEW_MAX_BORDER_WIDTH,
+ NULL);
+
+ return g_object_new (GIMP_TYPE_ITEM_CHOOSER,
+ "context", context,
+ "item-type", item_type,
+ "view-size", view_size,
+ "view-border-width", view_border_width,
+ NULL);
+}
+
+GimpItem *
+gimp_item_chooser_get_item (GimpItemChooser *chooser)
+{
+ g_return_val_if_fail (GIMP_IS_ITEM_CHOOSER (chooser), NULL);
+
+ return chooser->priv->item;
+}
+
+void
+gimp_item_chooser_set_item (GimpItemChooser *chooser,
+ GimpItem *item)
+{
+ if (! gtk_widget_in_destruction (GTK_WIDGET (chooser)))
+ {
+ g_signal_handlers_disconnect_by_func (chooser->priv->image_view,
+ G_CALLBACK (gimp_item_chooser_items_selected),
+ chooser);
+ if (chooser->priv->layer_view != NULL)
+ g_signal_handlers_disconnect_by_func (chooser->priv->layer_view,
+ G_CALLBACK (gimp_item_chooser_items_selected),
+ chooser);
+ else
+ g_return_if_fail (! GIMP_IS_LAYER (item));
+
+ if (chooser->priv->channel_view != NULL)
+ g_signal_handlers_disconnect_by_func (chooser->priv->channel_view,
+ G_CALLBACK (gimp_item_chooser_items_selected),
+ chooser);
+ else
+ g_return_if_fail (! GIMP_IS_CHANNEL (item));
+
+ if (chooser->priv->path_view != NULL)
+ g_signal_handlers_disconnect_by_func (chooser->priv->path_view,
+ G_CALLBACK (gimp_item_chooser_items_selected),
+ chooser);
+ else
+ g_return_if_fail (! GIMP_IS_PATH (item));
+
+ if (GIMP_IS_IMAGE (item))
+ {
+ gimp_container_view_set_1_selected (GIMP_CONTAINER_VIEW (chooser->priv->image_view),
+ GIMP_VIEWABLE (item));
+ gimp_context_set_image (chooser->priv->context, GIMP_IMAGE (item));
+ }
+ else if (GIMP_IS_LAYER (item))
+ {
+ gimp_context_set_image (chooser->priv->context, gimp_item_get_image (GIMP_ITEM (item)));
+ gimp_container_view_set_1_selected (GIMP_CONTAINER_VIEW (chooser->priv->layer_view),
+ GIMP_VIEWABLE (item));
+ }
+ else if (GIMP_IS_CHANNEL (item))
+ {
+ gimp_context_set_image (chooser->priv->context, gimp_item_get_image (GIMP_ITEM (item)));
+ gimp_container_view_set_1_selected (GIMP_CONTAINER_VIEW (chooser->priv->channel_view),
+ GIMP_VIEWABLE (item));
+ }
+ else if (GIMP_IS_PATH (item))
+ {
+ gimp_context_set_image (chooser->priv->context, gimp_item_get_image (GIMP_ITEM (item)));
+ gimp_container_view_set_1_selected (GIMP_CONTAINER_VIEW (chooser->priv->path_view),
+ GIMP_VIEWABLE (item));
+ }
+ else
+ {
+ g_return_if_fail (item == NULL);
+ gimp_container_view_set_1_selected (GIMP_CONTAINER_VIEW (chooser->priv->image_view), NULL);
+ gimp_context_set_image (chooser->priv->context, NULL);
+ }
+ g_signal_connect_object (chooser->priv->image_view, "selection-changed",
+ G_CALLBACK (gimp_item_chooser_items_selected),
+ G_OBJECT (chooser), 0);
+ if (chooser->priv->layer_view != NULL)
+ g_signal_connect_object (chooser->priv->layer_view, "selection-changed",
+ G_CALLBACK (gimp_item_chooser_items_selected),
+ G_OBJECT (chooser), 0);
+ if (chooser->priv->channel_view != NULL)
+ g_signal_connect_object (chooser->priv->channel_view, "selection-changed",
+ G_CALLBACK (gimp_item_chooser_items_selected),
+ G_OBJECT (chooser), 0);
+ if (chooser->priv->path_view != NULL)
+ g_signal_connect_object (chooser->priv->path_view, "selection-changed",
+ G_CALLBACK (gimp_item_chooser_items_selected),
+ G_OBJECT (chooser), 0);
+ }
+
+ if (item != chooser->priv->item)
+ {
+ g_clear_object (&chooser->priv->item);
+ chooser->priv->item = (item != NULL ? g_object_ref (item) : NULL);
+ g_object_notify (G_OBJECT (chooser), "item");
+ }
+}
+
+
+/* private functions */
+
+static void
+gimp_item_chooser_image_changed (GimpContext *context,
+ GimpImage *image,
+ GimpItemChooser *chooser)
+{
+ GimpContainer *layers = NULL;
+ GimpContainer *channels = NULL;
+ GimpContainer *paths = NULL;
+
+ if (image)
+ {
+ gchar *desc;
+
+ layers = gimp_image_get_layers (image);
+ channels = gimp_image_get_channels (image);
+ paths = gimp_image_get_paths (image);
+
+ desc = gimp_viewable_get_description (GIMP_VIEWABLE (image), NULL);
+ gtk_label_set_text (GTK_LABEL (chooser->priv->layer_label), desc);
+ g_free (desc);
+ }
+ else
+ {
+ gtk_label_set_text (GTK_LABEL (chooser->priv->layer_label),
+ _("Select an image in the left pane"));
+ }
+
+ g_signal_handlers_disconnect_by_func (chooser->priv->image_view,
+ G_CALLBACK (gimp_item_chooser_items_selected),
+ chooser);
+ if (chooser->priv->layer_view != NULL)
+ {
+ g_signal_handlers_disconnect_by_func (chooser->priv->layer_view,
+ G_CALLBACK (gimp_item_chooser_items_selected),
+ chooser);
+ gimp_container_view_set_container (GIMP_CONTAINER_VIEW (chooser->priv->layer_view),
+ layers);
+ g_signal_connect_object (chooser->priv->layer_view, "selection-changed",
+ G_CALLBACK (gimp_item_chooser_items_selected),
+ G_OBJECT (chooser), 0);
+ }
+ if (chooser->priv->channel_view != NULL)
+ {
+ g_signal_handlers_disconnect_by_func (chooser->priv->channel_view,
+ G_CALLBACK (gimp_item_chooser_items_selected),
+ chooser);
+ gimp_container_view_set_container (GIMP_CONTAINER_VIEW (chooser->priv->channel_view),
+ channels);
+ g_signal_connect_object (chooser->priv->channel_view, "selection-changed",
+ G_CALLBACK (gimp_item_chooser_items_selected),
+ G_OBJECT (chooser), 0);
+ }
+ if (chooser->priv->path_view != NULL)
+ {
+ g_signal_handlers_disconnect_by_func (chooser->priv->path_view,
+ G_CALLBACK (gimp_item_chooser_items_selected),
+ chooser);
+ gimp_container_view_set_container (GIMP_CONTAINER_VIEW (chooser->priv->path_view),
+ paths);
+ g_signal_connect_object (chooser->priv->path_view, "selection-changed",
+ G_CALLBACK (gimp_item_chooser_items_selected),
+ G_OBJECT (chooser), 0);
+ }
+ g_signal_connect_object (chooser->priv->image_view, "selection-changed",
+ G_CALLBACK (gimp_item_chooser_items_selected),
+ G_OBJECT (chooser), 0);
+}
+
+static void
+gimp_item_chooser_item_activated (GimpContainerView *view,
+ GimpItem *item,
+ GimpItemChooser *chooser)
+{
+ g_signal_emit (chooser, signals[ACTIVATE], 0, item);
+}
+
+static void
+gimp_item_chooser_items_selected (GimpContainerView *view,
+ GimpItemChooser *chooser)
+{
+ GimpItem *item = NULL;
+ gint n_items;
+ GList *items;
+
+ n_items = gimp_container_view_get_selected (view, &items);
+
+ g_return_if_fail (n_items <= 1);
+
+ if (items)
+ item = items->data;
+
+ gimp_item_chooser_set_item (chooser, item);
+}
diff --git a/app/widgets/gimpitemchooser.h b/app/widgets/gimpitemchooser.h
new file mode 100644
index 0000000000..0017dad652
--- /dev/null
+++ b/app/widgets/gimpitemchooser.h
@@ -0,0 +1,61 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpitemchooser.h
+ *
+ * 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 .
+ */
+
+#pragma once
+
+
+#define GIMP_TYPE_ITEM_CHOOSER (gimp_item_chooser_get_type ())
+#define GIMP_ITEM_CHOOSER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_ITEM_CHOOSER, GimpItemChooser))
+#define GIMP_ITEM_CHOOSER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_ITEM_CHOOSER, GimpItemChooserClass))
+#define GIMP_IS_ITEM_CHOOSER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_ITEM_CHOOSER))
+#define GIMP_IS_ITEM_CHOOSER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_ITEM_CHOOSER))
+#define GIMP_ITEM_CHOOSER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_ITEM_CHOOSER, GimpItemChooserClass))
+
+
+typedef struct _GimpItemChooserPrivate GimpItemChooserPrivate;
+typedef struct _GimpItemChooserClass GimpItemChooserClass;
+
+struct _GimpItemChooser
+{
+ GtkFrame parent_instance;
+
+ GimpItemChooserPrivate *priv;
+};
+
+struct _GimpItemChooserClass
+{
+ GtkFrameClass parent_instance;
+
+ /* Signals. */
+
+ void (* activate) (GimpItemChooser *view,
+ GimpItem *item);
+};
+
+
+GType gimp_item_chooser_get_type (void) G_GNUC_CONST;
+
+GtkWidget * gimp_item_chooser_new (GimpContext *context,
+ GType item_type,
+ gint view_size,
+ gint view_border_width);
+
+GimpItem * gimp_item_chooser_get_item (GimpItemChooser *chooser);
+void gimp_item_chooser_set_item (GimpItemChooser *chooser,
+ GimpItem *item);
diff --git a/app/widgets/gimpitemselect.c b/app/widgets/gimpitemselect.c
new file mode 100644
index 0000000000..241af3bca0
--- /dev/null
+++ b/app/widgets/gimpitemselect.c
@@ -0,0 +1,174 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpitemselect.c
+ *
+ * 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 .
+ */
+
+#include "config.h"
+
+#include
+#include
+
+#include "libgimpbase/gimpbase.h"
+#include "libgimpwidgets/gimpwidgets.h"
+
+#include "widgets-types.h"
+
+#include "gegl/gimp-babl-compat.h"
+
+#include "core/gimp.h"
+#include "core/gimpcontext.h"
+#include "core/gimpimage.h"
+#include "core/gimpitem.h"
+#include "core/gimpparamspecs.h"
+#include "core/gimptempbuf.h"
+
+#include "pdb/gimppdb.h"
+
+#include "gimpitemchooser.h"
+#include "gimpitemselect.h"
+#include "gimpcontainerbox.h"
+
+#include "gimp-intl.h"
+
+
+static void gimp_item_select_constructed (GObject *object);
+
+static GimpValueArray * gimp_item_select_run_callback (GimpPdbDialog *dialog,
+ GimpObject *object,
+ gboolean closing,
+ GError **error);
+static GimpObject * gimp_item_select_get_object (GimpPdbDialog *dialog);
+static void gimp_item_select_set_object (GimpPdbDialog *dialog,
+ GimpObject *object);
+
+static void gimp_item_select_activate (GimpItemSelect *select);
+static void gimp_item_select_notify_item (GimpItemSelect *select);
+
+
+G_DEFINE_TYPE (GimpItemSelect, gimp_item_select, GIMP_TYPE_PDB_DIALOG)
+
+#define parent_class gimp_item_select_parent_class
+
+
+static void
+gimp_item_select_class_init (GimpItemSelectClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GimpPdbDialogClass *pdb_class = GIMP_PDB_DIALOG_CLASS (klass);
+
+ object_class->constructed = gimp_item_select_constructed;
+
+ pdb_class->run_callback = gimp_item_select_run_callback;
+ pdb_class->get_object = gimp_item_select_get_object;
+ pdb_class->set_object = gimp_item_select_set_object;
+}
+
+static void
+gimp_item_select_init (GimpItemSelect *select)
+{
+}
+
+static void
+gimp_item_select_constructed (GObject *object)
+{
+ GimpPdbDialog *dialog = GIMP_PDB_DIALOG (object);
+ GimpItemSelect *select = GIMP_ITEM_SELECT (object);
+ GtkWidget *content_area;
+
+ G_OBJECT_CLASS (parent_class)->constructed (object);
+
+ content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
+
+ select->chooser = gimp_item_chooser_new (dialog->context, dialog->select_type,
+ GIMP_VIEW_SIZE_LARGE, 1);
+ gimp_item_chooser_set_item (GIMP_ITEM_CHOOSER (select->chooser),
+ GIMP_ITEM (dialog->initial_object));
+ g_signal_connect_swapped (select->chooser, "notify::item",
+ G_CALLBACK (gimp_item_select_notify_item),
+ select);
+ g_signal_connect_swapped (select->chooser, "activate",
+ G_CALLBACK (gimp_item_select_activate),
+ select);
+ gtk_box_pack_start (GTK_BOX (content_area), select->chooser, TRUE, TRUE, 0);
+ gtk_widget_set_visible (select->chooser, TRUE);
+}
+
+static GimpValueArray *
+gimp_item_select_run_callback (GimpPdbDialog *dialog,
+ GimpObject *object,
+ gboolean closing,
+ GError **error)
+{
+ GimpItem *item = GIMP_ITEM (object);
+ GimpValueArray *return_vals;
+
+ return_vals =
+ gimp_pdb_execute_procedure_by_name (dialog->pdb,
+ dialog->caller_context,
+ NULL, error,
+ dialog->callback_name,
+ GIMP_TYPE_ITEM, item,
+ G_TYPE_BOOLEAN, closing,
+ G_TYPE_NONE);
+
+ return return_vals;
+}
+
+static GimpObject *
+gimp_item_select_get_object (GimpPdbDialog *dialog)
+{
+ GimpItemSelect *select = GIMP_ITEM_SELECT (dialog);
+
+ return (GimpObject *) gimp_item_chooser_get_item (GIMP_ITEM_CHOOSER (select->chooser));
+}
+
+static void
+gimp_item_select_set_object (GimpPdbDialog *dialog,
+ GimpObject *object)
+{
+ GimpItemSelect *select = GIMP_ITEM_SELECT (dialog);
+
+ gimp_item_chooser_set_item (GIMP_ITEM_CHOOSER (select->chooser), GIMP_ITEM (object));
+}
+
+static void
+gimp_item_select_activate (GimpItemSelect *select)
+{
+ gimp_pdb_dialog_run_callback ((GimpPdbDialog **) &select, TRUE);
+ gtk_widget_destroy (GTK_WIDGET (select));
+}
+
+static void
+gimp_item_select_notify_item (GimpItemSelect *select)
+{
+ GtkWidget *button;
+ GimpItemChooser *chooser = GIMP_ITEM_CHOOSER (select->chooser);
+
+ button = gtk_dialog_get_widget_for_response (GTK_DIALOG (select),
+ GTK_RESPONSE_OK);
+
+ /* Clicking Okay when an image but not an item has been
+ * selected causes the widget to "lock up" and not reappear when you
+ * click on it again. For now, we'll disable the Okay button until
+ * an item is selected */
+ if (GIMP_IS_IMAGE (gimp_item_chooser_get_item (chooser)))
+ gtk_widget_set_sensitive (button, FALSE);
+ else
+ gtk_widget_set_sensitive (button, TRUE);
+
+ gimp_pdb_dialog_run_callback ((GimpPdbDialog **) &select, FALSE);
+}
diff --git a/app/widgets/gimpitemselect.h b/app/widgets/gimpitemselect.h
new file mode 100644
index 0000000000..365a633c27
--- /dev/null
+++ b/app/widgets/gimpitemselect.h
@@ -0,0 +1,48 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpitemselect.h
+ *
+ * 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 .
+ */
+
+#pragma once
+
+#include "gimppdbdialog.h"
+
+
+#define GIMP_TYPE_ITEM_SELECT (gimp_item_select_get_type ())
+#define GIMP_ITEM_SELECT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_ITEM_SELECT, GimpItemSelect))
+#define GIMP_ITEM_SELECT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_ITEM_SELECT, GimpItemSelectClass))
+#define GIMP_IS_ITEM_SELECT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_ITEM_SELECT))
+#define GIMP_IS_ITEM_SELECT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_ITEM_SELECT))
+#define GIMP_ITEM_SELECT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_ITEM_SELECT, GimpItemSelectClass))
+
+
+typedef struct _GimpItemSelectClass GimpItemSelectClass;
+
+struct _GimpItemSelect
+{
+ GimpPdbDialog parent_instance;
+
+ GtkWidget *chooser;
+};
+
+struct _GimpItemSelectClass
+{
+ GimpPdbDialogClass parent_class;
+};
+
+
+GType gimp_item_select_get_type (void) G_GNUC_CONST;
diff --git a/app/widgets/meson.build b/app/widgets/meson.build
index 8b63b0fe63..4b75ba84eb 100644
--- a/app/widgets/meson.build
+++ b/app/widgets/meson.build
@@ -145,6 +145,8 @@ libappwidgets_sources = [
'gimpimagepropview.c',
'gimpimageselect.c',
'gimpimageview.c',
+ 'gimpitemchooser.c',
+ 'gimpitemselect.c',
'gimpitemtreeview.c',
'gimpitemtreeview-search.c',
'gimplanguagecombobox.c',
diff --git a/app/widgets/widgets-types.h b/app/widgets/widgets-types.h
index 7dc5fb43b3..e59fb997be 100644
--- a/app/widgets/widgets-types.h
+++ b/app/widgets/widgets-types.h
@@ -163,6 +163,7 @@ typedef struct _GimpBrushSelect GimpBrushSelect;
typedef struct _GimpFontSelect GimpFontSelect;
typedef struct _GimpGradientSelect GimpGradientSelect;
typedef struct _GimpImageSelect GimpImageSelect;
+typedef struct _GimpItemSelect GimpItemSelect;
typedef struct _GimpPaletteSelect GimpPaletteSelect;
typedef struct _GimpPatternSelect GimpPatternSelect;
typedef struct _GimpPickableSelect GimpPickableSelect;
@@ -210,6 +211,7 @@ typedef struct _GimpImageCommentEditor GimpImageCommentEditor;
typedef struct _GimpImageParasiteView GimpImageParasiteView;
typedef struct _GimpImageProfileView GimpImageProfileView;
typedef struct _GimpImagePropView GimpImagePropView;
+typedef struct _GimpItemChooser GimpItemChooser;
typedef struct _GimpLanguageComboBox GimpLanguageComboBox;
typedef struct _GimpLanguageEntry GimpLanguageEntry;
typedef struct _GimpLanguageStore GimpLanguageStore;
diff --git a/libgimp/gimp.def b/libgimp/gimp.def
index e0eb48909a..4078b8ef5a 100644
--- a/libgimp/gimp.def
+++ b/libgimp/gimp.def
@@ -643,6 +643,9 @@ EXPORTS
gimp_item_transform_scale
gimp_item_transform_shear
gimp_item_transform_translate
+ gimp_items_close_popup
+ gimp_items_popup
+ gimp_items_set_popup
gimp_layer_add_alpha
gimp_layer_add_mask
gimp_layer_color_space_get_type
diff --git a/libgimp/gimpitem_pdb.c b/libgimp/gimpitem_pdb.c
index 577a3fb246..1b9cc65a80 100644
--- a/libgimp/gimpitem_pdb.c
+++ b/libgimp/gimpitem_pdb.c
@@ -1427,3 +1427,117 @@ gimp_item_get_parasite_list (GimpItem *item)
return parasites;
}
+
+/**
+ * gimp_items_popup:
+ * @callback: The callback PDB proc to call when user chooses an item.
+ * @popup_title: Title of the item selection dialog.
+ * @item_type: The name of the GIMP_TYPE_ITEM subtype.
+ * @initial_item: (nullable): The item to set as the initial choice.
+ * @parent_window: (nullable): An optional parent window handle for the popup to be set transient to.
+ *
+ * Invokes the item selection dialog.
+ *
+ * Opens a dialog letting a user choose an item .
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_items_popup (const gchar *callback,
+ const gchar *popup_title,
+ const gchar *item_type,
+ GimpItem *initial_item,
+ GBytes *parent_window)
+{
+ GimpValueArray *args;
+ GimpValueArray *return_vals;
+ gboolean success = TRUE;
+
+ args = gimp_value_array_new_from_types (NULL,
+ G_TYPE_STRING, callback,
+ G_TYPE_STRING, popup_title,
+ G_TYPE_STRING, item_type,
+ GIMP_TYPE_ITEM, initial_item,
+ G_TYPE_BYTES, parent_window,
+ G_TYPE_NONE);
+
+ return_vals = _gimp_pdb_run_procedure_array (gimp_get_pdb (),
+ "gimp-items-popup",
+ args);
+ gimp_value_array_unref (args);
+
+ success = GIMP_VALUES_GET_ENUM (return_vals, 0) == GIMP_PDB_SUCCESS;
+
+ gimp_value_array_unref (return_vals);
+
+ return success;
+}
+
+/**
+ * gimp_items_close_popup:
+ * @callback: The name of the callback registered for this pop-up.
+ *
+ * Close the item selection dialog.
+ *
+ * Closes an open item selection dialog.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_items_close_popup (const gchar *callback)
+{
+ GimpValueArray *args;
+ GimpValueArray *return_vals;
+ gboolean success = TRUE;
+
+ args = gimp_value_array_new_from_types (NULL,
+ G_TYPE_STRING, callback,
+ G_TYPE_NONE);
+
+ return_vals = _gimp_pdb_run_procedure_array (gimp_get_pdb (),
+ "gimp-items-close-popup",
+ args);
+ gimp_value_array_unref (args);
+
+ success = GIMP_VALUES_GET_ENUM (return_vals, 0) == GIMP_PDB_SUCCESS;
+
+ gimp_value_array_unref (return_vals);
+
+ return success;
+}
+
+/**
+ * gimp_items_set_popup:
+ * @callback: The name of the callback registered for this pop-up.
+ * @item: The item to set as selected.
+ *
+ * Sets the selected item in a item selection dialog.
+ *
+ * Sets the selected item in a item selection dialog.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_items_set_popup (const gchar *callback,
+ GimpItem *item)
+{
+ GimpValueArray *args;
+ GimpValueArray *return_vals;
+ gboolean success = TRUE;
+
+ args = gimp_value_array_new_from_types (NULL,
+ G_TYPE_STRING, callback,
+ GIMP_TYPE_ITEM, item,
+ G_TYPE_NONE);
+
+ return_vals = _gimp_pdb_run_procedure_array (gimp_get_pdb (),
+ "gimp-items-set-popup",
+ args);
+ gimp_value_array_unref (args);
+
+ success = GIMP_VALUES_GET_ENUM (return_vals, 0) == GIMP_PDB_SUCCESS;
+
+ gimp_value_array_unref (return_vals);
+
+ return success;
+}
diff --git a/libgimp/gimpitem_pdb.h b/libgimp/gimpitem_pdb.h
index a4801f6565..d417def45c 100644
--- a/libgimp/gimpitem_pdb.h
+++ b/libgimp/gimpitem_pdb.h
@@ -79,6 +79,14 @@ gboolean gimp_item_detach_parasite (GimpItem *item,
GimpParasite* gimp_item_get_parasite (GimpItem *item,
const gchar *name);
gchar** gimp_item_get_parasite_list (GimpItem *item);
+gboolean gimp_items_popup (const gchar *callback,
+ const gchar *popup_title,
+ const gchar *item_type,
+ GimpItem *initial_item,
+ GBytes *parent_window);
+gboolean gimp_items_close_popup (const gchar *callback);
+gboolean gimp_items_set_popup (const gchar *callback,
+ GimpItem *item);
G_END_DECLS
diff --git a/libgimp/gimpitemchooser.c b/libgimp/gimpitemchooser.c
new file mode 100644
index 0000000000..8a07cfaa32
--- /dev/null
+++ b/libgimp/gimpitemchooser.c
@@ -0,0 +1,630 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpitemchooser.h
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * .
+ */
+
+#include "config.h"
+
+#include
+#include
+
+#include "libgimpwidgets/gimpwidgets.h"
+
+#include "gimp.h"
+
+#include "gimpuitypes.h"
+#include "gimpitemchooser.h"
+#include "gimpuimarshal.h"
+
+#include "libgimp-intl.h"
+
+
+/**
+ * SECTION: gimpitemchooser
+ * @title: GimpItemChooser
+ * @short_description: A widget allowing to select an item.
+ *
+ * The chooser contains an optional label and a button which queries the core
+ * process to pop up a item selection dialog.
+ *
+ * Since: 3.0
+ **/
+
+#define CELL_SIZE 40
+
+enum
+{
+ PROP_0,
+ PROP_TITLE,
+ PROP_LABEL,
+ PROP_ITEM,
+ PROP_ITEM_TYPE,
+ N_PROPS
+};
+
+struct _GimpItemChooser
+{
+ GtkBox parent_instance;
+
+ GType item_type;
+ GimpItem *item;
+ gchar *title;
+ gchar *label;
+ gchar *callback;
+
+ GBytes *thumbnail;
+ GimpItem *thumbnail_item;
+ gint width;
+ gint height;
+ gint bpp;
+
+ GtkWidget *label_widget;
+ GtkWidget *preview_frame;
+ GtkWidget *preview;
+ GtkWidget *preview_title;
+};
+
+
+/* local function prototypes */
+
+static void gimp_item_chooser_constructed (GObject *object);
+static void gimp_item_chooser_dispose (GObject *object);
+static void gimp_item_chooser_finalize (GObject *object);
+
+static void gimp_item_chooser_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void gimp_item_chooser_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec);
+
+static void gimp_item_chooser_clicked (GimpItemChooser *chooser);
+
+static GimpValueArray * gimp_temp_callback_run (GimpProcedure *procedure,
+ GimpProcedureConfig *config,
+ GimpItemChooser *chooser);
+static gboolean gimp_item_select_remove_after_run (const gchar *procedure_name);
+
+static void gimp_item_chooser_draw (GimpItemChooser *chooser);
+static void gimp_item_chooser_get_thumbnail (GimpItemChooser *chooser,
+ gint width,
+ gint height);
+
+
+static GParamSpec *item_button_props[N_PROPS] = { NULL, };
+
+G_DEFINE_FINAL_TYPE (GimpItemChooser, gimp_item_chooser, GTK_TYPE_BOX)
+
+
+static void
+gimp_item_chooser_class_init (GimpItemChooserClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->constructed = gimp_item_chooser_constructed;
+ object_class->dispose = gimp_item_chooser_dispose;
+ object_class->finalize = gimp_item_chooser_finalize;
+ object_class->set_property = gimp_item_chooser_set_property;
+ object_class->get_property = gimp_item_chooser_get_property;
+
+ /**
+ * GimpItemChooser:title:
+ *
+ * The title to be used for the item selection popup dialog.
+ *
+ * Since: 3.0
+ */
+ item_button_props[PROP_TITLE] =
+ g_param_spec_string ("title",
+ "Title",
+ "The title to be used for the item selection popup dialog",
+ "Item Selection",
+ GIMP_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY);
+
+ /**
+ * GimpItemChooser:label:
+ *
+ * Label text with mnemonic.
+ *
+ * Since: 3.0
+ */
+ item_button_props[PROP_LABEL] =
+ g_param_spec_string ("label",
+ "Label",
+ "The label to be used next to the button",
+ NULL,
+ GIMP_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY);
+
+ /**
+ * GimpItemChooser:item:
+ *
+ * The currently selected item.
+ *
+ * Since: 3.0
+ */
+ item_button_props[PROP_ITEM] =
+ gimp_param_spec_item ("item",
+ "Item",
+ "The currently selected item",
+ TRUE,
+ GIMP_PARAM_READWRITE |
+ G_PARAM_EXPLICIT_NOTIFY);
+
+ /**
+ * GimpItemChooser:item-type:
+ *
+ * Allowed item types, which must be either GIMP_TYPE_ITEM or a
+ * subtype.
+ *
+ * Since: 3.0
+ */
+ item_button_props[PROP_ITEM_TYPE] =
+ g_param_spec_gtype ("item-type",
+ "Allowed item Type",
+ "The GType of the item property",
+ GIMP_TYPE_ITEM,
+ G_PARAM_CONSTRUCT_ONLY |
+ GIMP_PARAM_READWRITE);
+
+ g_object_class_install_properties (object_class,
+ N_PROPS, item_button_props);
+}
+
+static void
+gimp_item_chooser_init (GimpItemChooser *chooser)
+{
+ gtk_orientable_set_orientation (GTK_ORIENTABLE (chooser),
+ GTK_ORIENTATION_HORIZONTAL);
+ gtk_box_set_spacing (GTK_BOX (chooser), 6);
+
+ chooser->thumbnail_item = NULL;
+ chooser->thumbnail = NULL;
+}
+
+static void
+gimp_item_chooser_constructed (GObject *object)
+{
+ GimpItemChooser *chooser = GIMP_ITEM_CHOOSER (object);
+ GtkWidget *button;
+ GtkWidget *box;
+ gint scale_factor;
+
+ scale_factor = gtk_widget_get_scale_factor (GTK_WIDGET (chooser));
+
+ chooser->label_widget = gtk_label_new (NULL);
+ gtk_box_pack_start (GTK_BOX (chooser), chooser->label_widget, FALSE, FALSE, 0);
+ gtk_label_set_text_with_mnemonic (GTK_LABEL (chooser->label_widget), chooser->label);
+ gtk_widget_set_visible (GTK_WIDGET (chooser->label_widget), TRUE);
+
+ button = gtk_button_new ();
+ gtk_box_pack_start (GTK_BOX (chooser), button, FALSE, FALSE, 0);
+ gtk_label_set_mnemonic_widget (GTK_LABEL (chooser->label_widget), button);
+ gtk_widget_set_visible (button, TRUE);
+
+ box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 2);
+ gtk_container_add (GTK_CONTAINER (button), box);
+ gtk_widget_set_visible (box, TRUE);
+
+ chooser->preview_frame = gtk_aspect_frame_new (NULL, 0.5, 0.5, 1.0, FALSE);
+ gtk_frame_set_shadow_type (GTK_FRAME (chooser->preview_frame), GTK_SHADOW_IN);
+ gtk_box_pack_start (GTK_BOX (box), chooser->preview_frame, FALSE, FALSE, 0);
+ gtk_widget_set_visible (chooser->preview_frame, TRUE);
+
+ chooser->preview = gimp_preview_area_new ();
+ gtk_widget_set_size_request (chooser->preview, scale_factor * CELL_SIZE, scale_factor * CELL_SIZE);
+ gtk_container_add (GTK_CONTAINER (chooser->preview_frame), chooser->preview);
+ gtk_widget_set_visible (chooser->preview, TRUE);
+
+ chooser->preview_title = gtk_label_new (_("Browse..."));
+ gtk_box_pack_start (GTK_BOX (box), chooser->preview_title, FALSE, FALSE, 0);
+ gtk_widget_set_visible (chooser->preview_title, TRUE);
+
+ g_signal_connect_swapped (button, "clicked",
+ G_CALLBACK (gimp_item_chooser_clicked),
+ chooser);
+
+ G_OBJECT_CLASS (gimp_item_chooser_parent_class)->constructed (object);
+}
+
+static void
+gimp_item_chooser_dispose (GObject *object)
+{
+ GimpItemChooser *chooser = GIMP_ITEM_CHOOSER (object);
+
+ if (chooser->callback)
+ {
+ gimp_items_close_popup (chooser->callback);
+
+ gimp_plug_in_remove_temp_procedure (gimp_get_plug_in (),
+ chooser->callback);
+ g_clear_pointer (&chooser->callback, g_free);
+ }
+
+ G_OBJECT_CLASS (gimp_item_chooser_parent_class)->dispose (object);
+}
+
+static void
+gimp_item_chooser_finalize (GObject *object)
+{
+ GimpItemChooser *chooser = GIMP_ITEM_CHOOSER (object);
+
+ g_clear_pointer (&chooser->title, g_free);
+ g_clear_pointer (&chooser->label, g_free);
+ g_clear_pointer (&chooser->thumbnail, g_bytes_unref);
+
+ G_OBJECT_CLASS (gimp_item_chooser_parent_class)->finalize (object);
+}
+
+static void
+gimp_item_chooser_set_property (GObject *object,
+ guint property_id,
+ const GValue *gvalue,
+ GParamSpec *pspec)
+{
+ GimpItemChooser *chooser = GIMP_ITEM_CHOOSER (object);
+
+ switch (property_id)
+ {
+ case PROP_TITLE:
+ chooser->title = g_value_dup_string (gvalue);
+ break;
+
+ case PROP_LABEL:
+ chooser->label = g_value_dup_string (gvalue);
+ break;
+
+ case PROP_ITEM:
+ g_return_if_fail (g_value_get_object (gvalue) == NULL ||
+ g_type_is_a (G_TYPE_FROM_INSTANCE (g_value_get_object (gvalue)),
+ chooser->item_type));
+ gimp_item_chooser_set_item (chooser, g_value_get_object (gvalue));
+ break;
+
+ case PROP_ITEM_TYPE:
+ chooser->item_type = g_value_get_gtype (gvalue);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gimp_item_chooser_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GimpItemChooser *chooser = GIMP_ITEM_CHOOSER (object);
+
+ switch (property_id)
+ {
+ case PROP_TITLE:
+ g_value_set_string (value, chooser->title);
+ break;
+
+ case PROP_LABEL:
+ g_value_set_string (value, chooser->label);
+ break;
+
+ case PROP_ITEM:
+ g_value_set_object (value, chooser->item);
+ break;
+
+ case PROP_ITEM_TYPE:
+ g_value_set_gtype (value, chooser->item_type);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+/**
+ * gimp_item_chooser_new:
+ * @title: (nullable): Title of the dialog to use or %NULL to use the default title.
+ * @label: (nullable): Button label or %NULL for no label.
+ * @item_type: the acceptable subtype of choosable items.
+ * @item: (nullable): Initial item.
+ *
+ * Creates a new #GtkWidget that lets a user choose a item which must be of
+ * type @item_type. @item_type of values %G_TYPE_NONE and
+ * %GIMP_TYPE_ITEM are equivalent. Otherwise it must be a subtype of
+ * %GIMP_TYPE_ITEM.
+ *
+ * When @item is %NULL, initial choice is from context.
+ *
+ * Returns: A [class@GimpUi.ItemChooser.
+ *
+ * Since: 3.0
+ */
+GtkWidget *
+gimp_item_chooser_new (const gchar *title,
+ const gchar *label,
+ GType item_type,
+ GimpItem *item)
+{
+ GtkWidget *chooser;
+
+ if (item_type == G_TYPE_NONE)
+ item_type = GIMP_TYPE_ITEM;
+
+ g_return_val_if_fail (g_type_is_a (item_type, GIMP_TYPE_ITEM), NULL);
+ g_return_val_if_fail (item == NULL ||
+ g_type_is_a (G_TYPE_FROM_INSTANCE (item), item_type),
+ NULL);
+
+ chooser = g_object_new (GIMP_TYPE_ITEM_CHOOSER,
+ "title", title,
+ "label", label,
+ "item", item,
+ "item-type", item_type,
+ NULL);
+
+ return chooser;
+}
+
+/**
+ * gimp_item_chooser_get_item:
+ * @chooser: A #GimpItemChooser
+ *
+ * Gets the currently selected item.
+ *
+ * Returns: (transfer none): an internal copy of the item which must not be freed.
+ *
+ * Since: 3.0
+ */
+GimpItem *
+gimp_item_chooser_get_item (GimpItemChooser *chooser)
+{
+ g_return_val_if_fail (GIMP_IS_ITEM_CHOOSER (chooser), NULL);
+
+ return chooser->item;
+}
+
+/**
+ * gimp_item_chooser_set_item:
+ * @chooser: A #GimpItemChooser
+ * @item: Item to set.
+ *
+ * Sets the currently selected item.
+ * This will select the item in both the button and any chooser popup.
+ *
+ * Since: 3.0
+ */
+void
+gimp_item_chooser_set_item (GimpItemChooser *chooser,
+ GimpItem *item)
+{
+ g_return_if_fail (GIMP_IS_ITEM_CHOOSER (chooser));
+ g_return_if_fail (item == NULL || GIMP_IS_ITEM (item));
+
+ chooser->item = item;
+
+ if (chooser->callback)
+ gimp_items_set_popup (chooser->callback, chooser->item);
+
+ g_object_notify_by_pspec (G_OBJECT (chooser), item_button_props[PROP_ITEM]);
+
+ gimp_item_chooser_draw (chooser);
+}
+
+/**
+ * gimp_item_chooser_get_label:
+ * @widget: A [class@ItemChooser].
+ *
+ * Returns the label widget.
+ *
+ * Returns: (transfer none): the [class@Gtk.Widget] showing the label text.
+ * Since: 3.0
+ */
+GtkWidget *
+gimp_item_chooser_get_label (GimpItemChooser *chooser)
+{
+ g_return_val_if_fail (GIMP_IS_ITEM_CHOOSER (chooser), NULL);
+
+ return chooser->label_widget;
+}
+
+
+/* private functions */
+
+static GimpValueArray *
+gimp_temp_callback_run (GimpProcedure *procedure,
+ GimpProcedureConfig *config,
+ GimpItemChooser *chooser)
+{
+ GimpItem *item;
+ gboolean closing;
+
+ g_object_get (config,
+ "item", &item,
+ "closing", &closing,
+ NULL);
+
+ g_object_set (chooser, "item", item, NULL);
+
+ if (closing)
+ {
+ g_idle_add_full (G_PRIORITY_DEFAULT_IDLE,
+ (GSourceFunc) gimp_item_select_remove_after_run,
+ g_strdup (gimp_procedure_get_name (procedure)),
+ g_free);
+ g_clear_pointer (&chooser->callback, g_free);
+ }
+
+ g_clear_object (&item);
+
+ return gimp_procedure_new_return_values (procedure, GIMP_PDB_SUCCESS, NULL);
+}
+
+static gboolean
+gimp_item_select_remove_after_run (const gchar *procedure_name)
+{
+ gimp_plug_in_remove_temp_procedure (gimp_get_plug_in (), procedure_name);
+
+ return G_SOURCE_REMOVE;
+}
+
+static void
+gimp_item_chooser_clicked (GimpItemChooser *chooser)
+{
+ if (chooser->callback)
+ {
+ /* Popup already created. Calling setter raises the popup. */
+ gimp_items_set_popup (chooser->callback, chooser->item);
+ }
+ else
+ {
+ GimpPlugIn *plug_in = gimp_get_plug_in ();
+ GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (chooser));
+ GBytes *handle = NULL;
+ gchar *callback_name;
+ GimpProcedure *callback_procedure;
+
+ if (GIMP_IS_DIALOG (toplevel))
+ handle = gimp_dialog_get_native_handle (GIMP_DIALOG (toplevel));
+
+ callback_name = gimp_pdb_temp_procedure_name (gimp_get_pdb ());
+ callback_procedure = gimp_procedure_new (plug_in,
+ callback_name,
+ GIMP_PDB_PROC_TYPE_TEMPORARY,
+ (GimpRunFunc) gimp_temp_callback_run,
+ g_object_ref (chooser),
+ (GDestroyNotify) g_object_unref);
+ gimp_procedure_add_item_argument (callback_procedure, "item",
+ "Item", "The selected item",
+ TRUE, G_PARAM_READWRITE);
+ gimp_procedure_add_boolean_argument (callback_procedure, "closing",
+ "Closing", "If the dialog was closing",
+ FALSE, G_PARAM_READWRITE);
+
+ gimp_plug_in_add_temp_procedure (plug_in, callback_procedure);
+ g_object_unref (callback_procedure);
+ g_free (callback_name);
+
+ if (gimp_items_popup (gimp_procedure_get_name (callback_procedure), chooser->title,
+ g_type_name (chooser->item_type), chooser->item, handle))
+ {
+ /* Allow callbacks to be watched */
+ gimp_plug_in_persistent_enable (plug_in);
+
+ chooser->callback = g_strdup (gimp_procedure_get_name (callback_procedure));
+ }
+ else
+ {
+ g_warning ("%s: failed to open remote item select dialog.", G_STRFUNC);
+ gimp_plug_in_remove_temp_procedure (plug_in, gimp_procedure_get_name (callback_procedure));
+ return;
+ }
+ gimp_items_set_popup (chooser->callback, chooser->item);
+ }
+}
+
+static void
+gimp_item_chooser_draw (GimpItemChooser *chooser)
+{
+ GimpPreviewArea *area = GIMP_PREVIEW_AREA (chooser->preview);
+ GtkAllocation allocation;
+ gint x = 0;
+ gint y = 0;
+
+ gtk_widget_get_allocation (chooser->preview, &allocation);
+
+ gimp_item_chooser_get_thumbnail (chooser, allocation.width, allocation.height);
+
+ if (chooser->width < allocation.width ||
+ chooser->height < allocation.height)
+ {
+ x = ((allocation.width - chooser->width) / 2);
+ y = ((allocation.height - chooser->height) / 2);
+ }
+ gimp_preview_area_reset (area);
+
+ if (chooser->thumbnail)
+ {
+ GimpImageType type;
+ gint rowstride;
+
+ rowstride = chooser->width * chooser->bpp;
+ switch (chooser->bpp)
+ {
+ case 1:
+ type = GIMP_GRAY_IMAGE;
+ break;
+ case 2:
+ type = GIMP_GRAYA_IMAGE;
+ break;
+ case 3:
+ type = GIMP_RGB_IMAGE;
+ break;
+ case 4:
+ type = GIMP_RGBA_IMAGE;
+ break;
+ default:
+ g_return_if_reached ();
+ }
+
+ gimp_preview_area_draw (area, x, y, chooser->width, chooser->height, type,
+ g_bytes_get_data (chooser->thumbnail, NULL), rowstride);
+ }
+ else
+ {
+ /* TODO: GimpItems like GimpPath do not have a built-in drawable,
+ * but it is possible to get a preview from it. We should add a
+ * standard way to generate this preview. */
+ if (chooser->item)
+ gtk_label_set_text (GTK_LABEL (chooser->preview_title),
+ gimp_item_get_name (chooser->item));
+ }
+}
+
+static void
+gimp_item_chooser_get_thumbnail (GimpItemChooser *chooser,
+ gint width,
+ gint height)
+{
+ if (chooser->item == chooser->thumbnail_item &&
+ chooser->width == width &&
+ chooser->height == height)
+ /* Let's assume item contents is not changing in a single run. */
+ return;
+
+ g_clear_pointer (&chooser->thumbnail, g_bytes_unref);
+ chooser->width = chooser->height = 0;
+
+ chooser->thumbnail_item = chooser->item;
+ if (chooser->item)
+ {
+ if (GIMP_IS_DRAWABLE (chooser->item))
+ {
+ chooser->thumbnail =
+ gimp_drawable_get_thumbnail_data (GIMP_DRAWABLE (chooser->item),
+ width, height,
+ &chooser->width,
+ &chooser->height,
+ &chooser->bpp);
+ }
+ }
+}
diff --git a/libgimp/gimpitemchooser.h b/libgimp/gimpitemchooser.h
new file mode 100644
index 0000000000..00099fffa0
--- /dev/null
+++ b/libgimp/gimpitemchooser.h
@@ -0,0 +1,47 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpitemchooser.c
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * .
+ */
+
+#if !defined (__GIMP_UI_H_INSIDE__) && !defined (GIMP_COMPILATION)
+#error "Only can be included directly."
+#endif
+
+#ifndef __GIMP_ITEM_CHOOSER_H__
+#define __GIMP_ITEM_CHOOSER_H__
+
+G_BEGIN_DECLS
+
+#define GIMP_TYPE_ITEM_CHOOSER (gimp_item_chooser_get_type ())
+G_DECLARE_FINAL_TYPE (GimpItemChooser, gimp_item_chooser, GIMP, ITEM_CHOOSER, GtkBox)
+
+
+GtkWidget * gimp_item_chooser_new (const gchar *title,
+ const gchar *label,
+ GType item_type,
+ GimpItem *item);
+
+GimpItem * gimp_item_chooser_get_item (GimpItemChooser *chooser);
+void gimp_item_chooser_set_item (GimpItemChooser *chooser,
+ GimpItem *item);
+GtkWidget * gimp_item_chooser_get_label (GimpItemChooser *widget);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_ITEM_CHOOSER_H__ */
diff --git a/libgimp/gimpproceduredialog.c b/libgimp/gimpproceduredialog.c
index ff215d4451..8c6a231016 100644
--- a/libgimp/gimpproceduredialog.c
+++ b/libgimp/gimpproceduredialog.c
@@ -913,6 +913,10 @@ gimp_procedure_dialog_get_widget (GimpProcedureDialog *dialog,
{
widget = gimp_prop_drawable_chooser_new (G_OBJECT (priv->config), property, NULL);
}
+ else if (G_IS_PARAM_SPEC_OBJECT (pspec) && (pspec->value_type == GIMP_TYPE_PATH))
+ {
+ widget = gimp_prop_item_chooser_new (G_OBJECT (priv->config), property, NULL);
+ }
else if (G_IS_PARAM_SPEC_OBJECT (pspec) && pspec->value_type == GIMP_TYPE_IMAGE)
{
widget = gimp_prop_image_chooser_new (G_OBJECT (priv->config), property, NULL);
@@ -966,6 +970,8 @@ gimp_procedure_dialog_get_widget (GimpProcedureDialog *dialog,
label = gimp_resource_chooser_get_label (GIMP_RESOURCE_CHOOSER (widget));
else if (GIMP_IS_DRAWABLE_CHOOSER (widget))
label = gimp_drawable_chooser_get_label (GIMP_DRAWABLE_CHOOSER (widget));
+ else if (GIMP_IS_ITEM_CHOOSER (widget))
+ label = gimp_item_chooser_get_label (GIMP_ITEM_CHOOSER (widget));
else if (GIMP_IS_IMAGE_CHOOSER (widget))
label = gimp_image_chooser_get_label (GIMP_IMAGE_CHOOSER (widget));
}
@@ -2922,6 +2928,10 @@ gimp_procedure_dialog_check_mnemonic (GimpProcedureDialog *dialog,
{
label = gimp_drawable_chooser_get_label (GIMP_DRAWABLE_CHOOSER (widget));
}
+ else if (GIMP_IS_ITEM_CHOOSER (widget))
+ {
+ label = gimp_item_chooser_get_label (GIMP_ITEM_CHOOSER (widget));
+ }
else if (GIMP_IS_IMAGE_CHOOSER (widget))
{
label = gimp_image_chooser_get_label (GIMP_IMAGE_CHOOSER (widget));
diff --git a/libgimp/gimppropwidgets.c b/libgimp/gimppropwidgets.c
index 76c42c57c1..c1ba42c8e5 100644
--- a/libgimp/gimppropwidgets.c
+++ b/libgimp/gimppropwidgets.c
@@ -206,6 +206,74 @@ gimp_prop_image_chooser_new (GObject *config,
return prop_chooser;
}
+/**
+ * gimp_prop_item_chooser_new:
+ * @config: Object to which property is attached.
+ * @property_name: Name of a [class@Gimp.Item] property.
+ * @chooser_title: (nullable): title for the poppable dialog.
+ *
+ * Creates a [class@GimpUi.ItemChooser] controlled by the specified property.
+ *
+ * Returns: (transfer full): A new [class@GimpUi.ItemChooser].
+ *
+ * Since: 3.0
+ */
+GtkWidget *
+gimp_prop_item_chooser_new (GObject *config,
+ const gchar *property_name,
+ const gchar *chooser_title)
+{
+ GParamSpec *param_spec;
+ GtkWidget *prop_chooser;
+ GimpItem *initial_item = NULL;
+ gchar *title = NULL;
+ const gchar *label;
+
+ param_spec = g_object_class_find_property (G_OBJECT_GET_CLASS (config),
+ property_name);
+
+ g_return_val_if_fail (param_spec != NULL, NULL);
+ g_return_val_if_fail (g_type_is_a (G_TYPE_FROM_INSTANCE (param_spec), G_TYPE_PARAM_OBJECT) &&
+ g_type_is_a (param_spec->value_type, GIMP_TYPE_ITEM), NULL);
+
+ g_object_get (config,
+ property_name, &initial_item,
+ NULL);
+
+ label = g_param_spec_get_nick (param_spec);
+
+ if (chooser_title == NULL)
+ {
+ gchar *canonical;
+
+ canonical = gimp_utils_make_canonical_menu_label (label);
+ if (g_type_is_a (param_spec->value_type, GIMP_TYPE_LAYER))
+ title = g_strdup_printf (_("Choose layer: %s"), canonical);
+ if (g_type_is_a (param_spec->value_type, GIMP_TYPE_CHANNEL))
+ title = g_strdup_printf (_("Choose channel: %s"), canonical);
+ if (g_type_is_a (param_spec->value_type, GIMP_TYPE_PATH))
+ title = g_strdup_printf (_("Choose path: %s"), canonical);
+ else
+ title = g_strdup_printf (_("Choose item: %s"), canonical);
+ g_free (canonical);
+ }
+ else
+ {
+ title = g_strdup (chooser_title);
+ }
+
+ prop_chooser = gimp_item_chooser_new (title, label, param_spec->value_type,
+ initial_item);
+ g_clear_object (&initial_item);
+ g_free (title);
+
+ g_object_bind_property (prop_chooser, "item",
+ config, property_name,
+ G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
+
+ return prop_chooser;
+}
+
/**
* gimp_prop_drawable_chooser_new:
* @config: Object to which property is attached.
diff --git a/libgimp/gimppropwidgets.h b/libgimp/gimppropwidgets.h
index dddc006a8a..32c2022074 100644
--- a/libgimp/gimppropwidgets.h
+++ b/libgimp/gimppropwidgets.h
@@ -51,6 +51,9 @@ GtkWidget * gimp_prop_pattern_chooser_new (GObject *config,
GtkWidget * gimp_prop_image_chooser_new (GObject *config,
const gchar *property_name,
const gchar *chooser_title);
+GtkWidget * gimp_prop_item_chooser_new (GObject *config,
+ const gchar *property_name,
+ const gchar *chooser_title);
GtkWidget * gimp_prop_drawable_chooser_new (GObject *config,
const gchar *property_name,
const gchar *chooser_title);
diff --git a/libgimp/gimpui.def b/libgimp/gimpui.def
index e7453c96b8..947c3f2a35 100644
--- a/libgimp/gimpui.def
+++ b/libgimp/gimpui.def
@@ -29,6 +29,11 @@ EXPORTS
gimp_image_chooser_set_image
gimp_image_combo_box_get_type
gimp_image_combo_box_new
+ gimp_item_chooser_get_item
+ gimp_item_chooser_get_label
+ gimp_item_chooser_get_type
+ gimp_item_chooser_new
+ gimp_item_chooser_set_item
gimp_layer_combo_box_get_type
gimp_layer_combo_box_new
gimp_palette_chooser_get_type
@@ -76,6 +81,7 @@ EXPORTS
gimp_prop_font_chooser_new
gimp_prop_gradient_chooser_new
gimp_prop_image_chooser_new
+ gimp_prop_item_chooser_new
gimp_prop_palette_chooser_new
gimp_prop_pattern_chooser_new
gimp_prop_resolution_entry_new
diff --git a/libgimp/gimpui.h b/libgimp/gimpui.h
index 81570cc854..f3818535e7 100644
--- a/libgimp/gimpui.h
+++ b/libgimp/gimpui.h
@@ -37,6 +37,7 @@
#include
#include
#include
+#include
#include
#include
#include
diff --git a/libgimp/gimpuitypes.h b/libgimp/gimpuitypes.h
index eb9ad8810a..7ed4814bf6 100644
--- a/libgimp/gimpuitypes.h
+++ b/libgimp/gimpuitypes.h
@@ -51,6 +51,7 @@ typedef struct _GimpBrushChooser GimpBrushChooser;
typedef struct _GimpFontChooser GimpFontChooser;
typedef struct _GimpGradientChooser GimpGradientChooser;
typedef struct _GimpImageChooser GimpImageChooser;
+typedef struct _GimpItemChooser GimpItemChooser;
typedef struct _GimpPaletteChooser GimpPaletteChooser;
typedef struct _GimpPatternChooser GimpPatternChooser;
diff --git a/libgimp/meson.build b/libgimp/meson.build
index acb0580d61..c12e60c2cd 100644
--- a/libgimp/meson.build
+++ b/libgimp/meson.build
@@ -305,6 +305,7 @@ libgimpui_sources_introspectable = [
'gimpgradientchooser.c',
'gimpimagechooser.c',
'gimpimagecombobox.c',
+ 'gimpitemchooser.c',
'gimpitemcombobox.c',
'gimppalettechooser.c',
'gimppatternchooser.c',
@@ -342,6 +343,7 @@ libgimpui_headers_introspectable = [
'gimpgradientchooser.h',
'gimpimagechooser.h',
'gimpimagecombobox.h',
+ 'gimpitemchooser.h',
'gimpitemcombobox.h',
'gimppalettechooser.h',
'gimppatternchooser.h',
diff --git a/pdb/groups/item.pdb b/pdb/groups/item.pdb
index 6b5fcf7f86..0abad7c502 100644
--- a/pdb/groups/item.pdb
+++ b/pdb/groups/item.pdb
@@ -1121,7 +1121,92 @@ CODE
);
}
-@headers = qw("core/gimpgrouplayer.h"
+sub items_popup {
+ $blurb = 'Invokes the item selection dialog.';
+ $help = 'Opens a dialog letting a user choose an item .';
+
+ &alxsa_pdb_misc('2025');
+
+ @inargs = (
+ { name => 'callback', type => 'string', non_empty => 1,
+ desc => 'The callback PDB proc to call when user chooses an item ' },
+ { name => 'popup_title', type => 'string',
+ desc => 'Title of the item selection dialog' },
+ { name => 'item_type', type => 'string', non_empty => 1,
+ desc => 'The name of the GIMP_TYPE_ITEM subtype' },
+ { name => 'initial_item', type => 'item ', none_ok => 1, no_validate => 1,
+ desc => 'The item to set as the initial choice' },
+ { name => 'parent_window', type => 'bytes', none_ok => 1,
+ desc => 'An optional parent window handle for the popup to be set transient to' }
+ );
+
+ %invoke = (
+ code => <<'CODE'
+{
+ if (gimp->no_interface ||
+ ! gimp_pdb_lookup_procedure (gimp->pdb, callback) ||
+ ! gimp_pdb_dialog_new (gimp, context, progress,
+ g_type_from_name (item_type),
+ parent_window, popup_title, callback,
+ GIMP_OBJECT (initial_item),
+ NULL))
+ success = FALSE;
+}
+CODE
+ );
+}
+
+sub items_close_popup {
+ $blurb = 'Close the item selection dialog.';
+ $help = 'Closes an open item selection dialog.';
+
+ &alxsa_pdb_misc('2025');
+
+ @inargs = (
+ { name => 'callback', type => 'string', non_empty => 1,
+ desc => 'The name of the callback registered for this pop-up' }
+ );
+
+ %invoke = (
+ code => <<'CODE'
+{
+ if (gimp->no_interface ||
+ ! gimp_pdb_lookup_procedure (gimp->pdb, callback) ||
+ ! gimp_pdb_dialog_close (gimp, GIMP_TYPE_ITEM, callback))
+ success = FALSE;
+}
+CODE
+ );
+}
+
+sub items_set_popup {
+ $blurb = 'Sets the selected item in a item selection dialog.';
+ $help = $blurb;
+
+ &alxsa_pdb_misc('2025');
+
+ @inargs = (
+ { name => 'callback', type => 'string', non_empty => 1,
+ desc => 'The name of the callback registered for this pop-up' },
+ { name => 'item', type => 'item', no_validate => 1,
+ desc => 'The item to set as selected' }
+ );
+
+ %invoke = (
+ code => <<'CODE'
+{
+ if (gimp->no_interface ||
+ ! gimp_pdb_lookup_procedure (gimp->pdb, callback) ||
+ ! gimp_pdb_dialog_set (gimp, GIMP_TYPE_ITEM, callback, GIMP_OBJECT (item), NULL))
+ success = FALSE;
+}
+CODE
+ );
+}
+
+@headers = qw("core/gimp.h"
+ "core/gimpdatafactory.h"
+ "core/gimpgrouplayer.h"
"core/gimplayermask.h"
"core/gimplinklayer.h"
"core/gimplist.h"
@@ -1159,7 +1244,10 @@ CODE
item_get_tattoo item_set_tattoo
item_attach_parasite item_detach_parasite
item_get_parasite
- item_get_parasite_list);
+ item_get_parasite_list
+ items_popup
+ items_close_popup
+ items_set_popup);
%exports = (app => [@procs], lib => [@procs]);