From 36576c72760bb3d62a124fddd3be38b1bc12ab8e Mon Sep 17 00:00:00 2001 From: Jehan Date: Mon, 28 Aug 2023 21:42:39 +0200 Subject: [PATCH] =?UTF-8?q?libgimp:=20new=20GimpDrawableChooser=20widget,?= =?UTF-8?q?=20associated=20propwidget=20function=20and=E2=80=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit … generation of such a widget for drawable arguments in PDB procedures. --- libgimp/gimpdrawablechooser.c | 572 ++++++++++++++++++++++++++++++++++ libgimp/gimpdrawablechooser.h | 47 +++ libgimp/gimpproceduredialog.c | 14 + libgimp/gimppropwidgets.c | 89 +++++- libgimp/gimppropwidgets.h | 4 + libgimp/gimpui.def | 6 + libgimp/gimpui.h | 3 +- libgimp/gimpuitypes.h | 2 + libgimp/meson.build | 2 + po-libgimp/POTFILES.in | 6 +- 10 files changed, 738 insertions(+), 7 deletions(-) create mode 100644 libgimp/gimpdrawablechooser.c create mode 100644 libgimp/gimpdrawablechooser.h diff --git a/libgimp/gimpdrawablechooser.c b/libgimp/gimpdrawablechooser.c new file mode 100644 index 0000000000..fde58873d3 --- /dev/null +++ b/libgimp/gimpdrawablechooser.c @@ -0,0 +1,572 @@ +/* LIBGIMP - The GIMP Library + * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball + * + * gimpdrawablechooser.h + * Copyright (C) 2023 Jehan + * + * 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 "gimpdrawablechooser.h" +#include "gimpuimarshal.h" + +#include "libgimp-intl.h" + + +/** + * SECTION: gimpdrawablechooser + * @title: GimpDrawableChooser + * @short_description: A widget allowing to select a drawable. + * + * The chooser contains an optional label and a button which queries the core + * process to pop up a drawable selection dialog. + * + * Since: 3.0 + **/ + +#define CELL_SIZE 40 + +enum +{ + PROP_0, + PROP_TITLE, + PROP_LABEL, + PROP_DRAWABLE, + N_PROPS +}; + +struct _GimpDrawableChooser +{ + GtkBox parent_instance; + + GimpDrawable *drawable; + gchar *title; + gchar *label; + gchar *callback; + + GBytes *thumbnail; + GimpDrawable *thumbnail_drawable; + gint width; + gint height; + gint bpp; + + GtkWidget *label_widget; + GtkWidget *preview_frame; + GtkWidget *preview; + GtkWidget *preview_title; +}; + + +/* local function prototypes */ + +static void gimp_drawable_chooser_constructed (GObject *object); +static void gimp_drawable_chooser_dispose (GObject *object); +static void gimp_drawable_chooser_finalize (GObject *object); + +static void gimp_drawable_chooser_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); +static void gimp_drawable_chooser_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); + +static void gimp_drawable_chooser_clicked (GimpDrawableChooser *chooser); + +static GimpValueArray * gimp_temp_callback_run (GimpProcedure *procedure, + GimpProcedureConfig *config, + GimpDrawableChooser *chooser); +static gboolean gimp_drawable_select_remove_after_run (const gchar *procedure_name); + +static void gimp_drawable_chooser_draw (GimpDrawableChooser *chooser); +static void gimp_drawable_chooser_get_thumbnail (GimpDrawableChooser *chooser, + gint width, + gint height); + + +static GParamSpec *drawable_button_props[N_PROPS] = { NULL, }; + +G_DEFINE_FINAL_TYPE (GimpDrawableChooser, gimp_drawable_chooser, GTK_TYPE_BOX) + + +static void +gimp_drawable_chooser_class_init (GimpDrawableChooserClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->constructed = gimp_drawable_chooser_constructed; + object_class->dispose = gimp_drawable_chooser_dispose; + object_class->finalize = gimp_drawable_chooser_finalize; + object_class->set_property = gimp_drawable_chooser_set_property; + object_class->get_property = gimp_drawable_chooser_get_property; + + /** + * GimpDrawableChooser:title: + * + * The title to be used for the drawable selection popup dialog. + * + * Since: 3.0 + */ + drawable_button_props[PROP_TITLE] = + g_param_spec_string ("title", + "Title", + "The title to be used for the drawable selection popup dialog", + "Drawable Selection", + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY); + + /** + * GimpDrawableChooser:label: + * + * Label text with mnemonic. + * + * Since: 3.0 + */ + drawable_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); + + /** + * GimpDrawableChooser:drawable: + * + * The currently selected drawable. + * + * Since: 3.0 + */ + drawable_button_props[PROP_DRAWABLE] = + gimp_param_spec_drawable ("drawable", + "Drawable", + "The currently selected drawable", + TRUE, + GIMP_PARAM_READWRITE | + G_PARAM_EXPLICIT_NOTIFY); + + g_object_class_install_properties (object_class, + N_PROPS, drawable_button_props); +} + +static void +gimp_drawable_chooser_init (GimpDrawableChooser *chooser) +{ + gtk_orientable_set_orientation (GTK_ORIENTABLE (chooser), + GTK_ORIENTATION_HORIZONTAL); + gtk_box_set_spacing (GTK_BOX (chooser), 6); + + chooser->thumbnail_drawable = NULL; + chooser->thumbnail = NULL; +} + +static void +gimp_drawable_chooser_constructed (GObject *object) +{ + GimpDrawableChooser *chooser = GIMP_DRAWABLE_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_show (GTK_WIDGET (chooser->label_widget)); + + 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_show (button); + + box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 2); + gtk_container_add (GTK_CONTAINER (button), box); + gtk_widget_show (box); + + 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_show (chooser->preview_frame); + + 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_show (chooser->preview); + + chooser->preview_title = gtk_label_new (_("Browse...")); + gtk_box_pack_start (GTK_BOX (box), chooser->preview_title, FALSE, FALSE, 0); + gtk_widget_show (chooser->preview_title); + + g_signal_connect_swapped (button, "clicked", + G_CALLBACK (gimp_drawable_chooser_clicked), + chooser); + + G_OBJECT_CLASS (gimp_drawable_chooser_parent_class)->constructed (object); +} + +static void +gimp_drawable_chooser_dispose (GObject *object) +{ + GimpDrawableChooser *chooser = GIMP_DRAWABLE_CHOOSER (object); + + if (chooser->callback) + { + gimp_drawables_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_drawable_chooser_parent_class)->dispose (object); +} + +static void +gimp_drawable_chooser_finalize (GObject *object) +{ + GimpDrawableChooser *chooser = GIMP_DRAWABLE_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_drawable_chooser_parent_class)->finalize (object); +} + +static void +gimp_drawable_chooser_set_property (GObject *object, + guint property_id, + const GValue *gvalue, + GParamSpec *pspec) +{ + GimpDrawableChooser *chooser = GIMP_DRAWABLE_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_DRAWABLE: + gimp_drawable_chooser_set_drawable (chooser, g_value_get_object (gvalue)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_drawable_chooser_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpDrawableChooser *chooser = GIMP_DRAWABLE_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_DRAWABLE: + g_value_set_object (value, chooser->drawable); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +/** + * gimp_drawable_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. + * @drawable: (nullable): Initial drawable. + * + * Creates a new #GtkWidget that lets a user choose a drawable. + * + * When @drawable is %NULL, initial choice is from context. + * + * Returns: A [class@Gimp.DrawableChooser. + * + * Since: 3.0 + */ +GtkWidget * +gimp_drawable_chooser_new (const gchar *title, + const gchar *label, + GimpDrawable *drawable) +{ + GtkWidget *chooser; + + chooser = g_object_new (GIMP_TYPE_DRAWABLE_CHOOSER, + "title", title, + "label", label, + "drawable", drawable, + NULL); + + return chooser; +} + +/** + * gimp_drawable_chooser_get_drawable: + * @chooser: A #GimpDrawableChooser + * + * Gets the currently selected drawable. + * + * Returns: (transfer none): an internal copy of the drawable which must not be freed. + * + * Since: 3.0 + */ +GimpDrawable * +gimp_drawable_chooser_get_drawable (GimpDrawableChooser *chooser) +{ + g_return_val_if_fail (GIMP_IS_DRAWABLE_CHOOSER (chooser), NULL); + + return chooser->drawable; +} + +/** + * gimp_drawable_chooser_set_drawable: + * @chooser: A #GimpDrawableChooser + * @drawable: Drawable to set. + * + * Sets the currently selected drawable. + * This will select the drawable in both the button and any chooser popup. + * + * Since: 3.0 + */ +void +gimp_drawable_chooser_set_drawable (GimpDrawableChooser *chooser, + GimpDrawable *drawable) +{ + g_return_if_fail (GIMP_IS_DRAWABLE_CHOOSER (chooser)); + g_return_if_fail (drawable == NULL || GIMP_IS_DRAWABLE (drawable)); + + chooser->drawable = drawable; + + if (chooser->callback) + gimp_drawables_set_popup (chooser->callback, chooser->drawable); + + g_object_notify_by_pspec (G_OBJECT (chooser), drawable_button_props[PROP_DRAWABLE]); + + gimp_drawable_chooser_draw (chooser); +} + +/** + * gimp_drawable_chooser_get_label: + * @widget: A [class@DrawableChooser]. + * + * Returns the label widget. + * + * Returns: (transfer none): the [class@Gtk.Widget] showing the label text. + * Since: 3.0 + */ +GtkWidget * +gimp_drawable_chooser_get_label (GimpDrawableChooser *chooser) +{ + g_return_val_if_fail (GIMP_IS_DRAWABLE_CHOOSER (chooser), NULL); + + return chooser->label_widget; +} + + +/* private functions */ + +static GimpValueArray * +gimp_temp_callback_run (GimpProcedure *procedure, + GimpProcedureConfig *config, + GimpDrawableChooser *chooser) +{ + GimpDrawable *drawable; + gboolean closing; + + g_object_get (config, + "drawable", &drawable, + "closing", &closing, + NULL); + + g_object_set (chooser, "drawable", drawable, NULL); + + if (closing) + { + g_idle_add_full (G_PRIORITY_DEFAULT_IDLE, + (GSourceFunc) gimp_drawable_select_remove_after_run, + g_strdup (gimp_procedure_get_name (procedure)), + g_free); + g_clear_pointer (&chooser->callback, g_free); + } + + g_clear_object (&drawable); + + return gimp_procedure_new_return_values (procedure, GIMP_PDB_SUCCESS, NULL); +} + +static gboolean +gimp_drawable_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_drawable_chooser_clicked (GimpDrawableChooser *chooser) +{ + if (chooser->callback) + { + /* Popup already created. Calling setter raises the popup. */ + gimp_drawables_set_popup (chooser->callback, chooser->drawable); + } + 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_PROC_ARG_DRAWABLE (callback_procedure, "drawable", + "Drawable", "The selected drawable", + TRUE, G_PARAM_READWRITE); + GIMP_PROC_ARG_BOOLEAN (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_drawables_popup (gimp_procedure_get_name (callback_procedure), chooser->title, + chooser->drawable, handle)) + { + /* Allow callbacks to be watched */ + gimp_plug_in_extension_enable (plug_in); + + chooser->callback = g_strdup (gimp_procedure_get_name (callback_procedure)); + } + else + { + g_warning ("%s: failed to open remote drawable select dialog.", G_STRFUNC); + gimp_plug_in_remove_temp_procedure (plug_in, gimp_procedure_get_name (callback_procedure)); + return; + } + gimp_drawables_set_popup (chooser->callback, chooser->drawable); + } +} + +static void +gimp_drawable_chooser_draw (GimpDrawableChooser *chooser) +{ + GimpPreviewArea *area = GIMP_PREVIEW_AREA (chooser->preview); + GtkAllocation allocation; + gint x = 0; + gint y = 0; + + gtk_widget_get_allocation (chooser->preview, &allocation); + + gimp_drawable_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); + } +} + +static void +gimp_drawable_chooser_get_thumbnail (GimpDrawableChooser *chooser, + gint width, + gint height) +{ + if (chooser->drawable == chooser->thumbnail_drawable && + chooser->width == width && + chooser->height == height) + /* Let's assume drawable contents is not changing in a single run. */ + return; + + g_clear_pointer (&chooser->thumbnail, g_bytes_unref); + chooser->width = chooser->height = 0; + + chooser->thumbnail_drawable = chooser->drawable; + if (chooser->drawable) + chooser->thumbnail = gimp_drawable_get_thumbnail_data (chooser->drawable, + width, height, + &chooser->width, + &chooser->height, + &chooser->bpp); +} diff --git a/libgimp/gimpdrawablechooser.h b/libgimp/gimpdrawablechooser.h new file mode 100644 index 0000000000..e325209e06 --- /dev/null +++ b/libgimp/gimpdrawablechooser.h @@ -0,0 +1,47 @@ +/* LIBGIMP - The GIMP Library + * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball + * + * gimpdrawablechooser.c + * Copyright (C) 2023 Jehan + * + * 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_DRAWABLE_CHOOSER_H__ +#define __GIMP_DRAWABLE_CHOOSER_H__ + +G_BEGIN_DECLS + +#define GIMP_TYPE_DRAWABLE_CHOOSER (gimp_drawable_chooser_get_type ()) +G_DECLARE_FINAL_TYPE (GimpDrawableChooser, gimp_drawable_chooser, GIMP, DRAWABLE_CHOOSER, GtkBox) + + +GtkWidget * gimp_drawable_chooser_new (const gchar *title, + const gchar *label, + GimpDrawable *drawable); + +GimpDrawable * gimp_drawable_chooser_get_drawable (GimpDrawableChooser *chooser); +void gimp_drawable_chooser_set_drawable (GimpDrawableChooser *chooser, + GimpDrawable *drawable); +GtkWidget * gimp_drawable_chooser_get_label (GimpDrawableChooser *widget); + + +G_END_DECLS + +#endif /* __GIMP_DRAWABLE_CHOOSER_H__ */ diff --git a/libgimp/gimpproceduredialog.c b/libgimp/gimpproceduredialog.c index df57016a16..2512628d16 100644 --- a/libgimp/gimpproceduredialog.c +++ b/libgimp/gimpproceduredialog.c @@ -795,6 +795,10 @@ gimp_procedure_dialog_get_widget (GimpProcedureDialog *dialog, { widget = gimp_prop_pattern_chooser_new (G_OBJECT (dialog->priv->config), property, _("Pattern Chooser")); } + else if (G_IS_PARAM_SPEC_OBJECT (pspec) && pspec->value_type == GIMP_TYPE_DRAWABLE) + { + widget = gimp_prop_drawable_chooser_new (G_OBJECT (dialog->priv->config), property, NULL); + } else if (G_PARAM_SPEC_TYPE (pspec) == G_TYPE_PARAM_ENUM) { GimpIntStore *store; @@ -838,6 +842,8 @@ gimp_procedure_dialog_get_widget (GimpProcedureDialog *dialog, label = gimp_labeled_get_label (GIMP_LABELED (widget)); else if (GIMP_IS_RESOURCE_CHOOSER (widget)) 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)); } if (label != NULL) gtk_size_group_add_widget (dialog->priv->label_group, label); @@ -2582,6 +2588,14 @@ gimp_procedure_dialog_check_mnemonic (GimpProcedureDialog *dialog, { label = gimp_labeled_get_label (GIMP_LABELED (widget)); } + else if (GIMP_IS_RESOURCE_CHOOSER (widget)) + { + 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 { GList *labels = gtk_widget_list_mnemonic_labels (widget); diff --git a/libgimp/gimppropwidgets.c b/libgimp/gimppropwidgets.c index ebbcf57e70..7c9abd8f3f 100644 --- a/libgimp/gimppropwidgets.c +++ b/libgimp/gimppropwidgets.c @@ -18,6 +18,8 @@ #include "libgimp/gimpui.h" +#include "libgimp-intl.h" + /** * SECTION: gimppropwidgets * @title: GimpPropWidgets @@ -35,10 +37,11 @@ typedef GtkWidget* (*GimpResourceWidgetCreator) (const gchar *title, /* utility function prototypes */ -static GtkWidget * gimp_prop_resource_chooser_factory (GimpResourceWidgetCreator widget_creator_func, - GObject *config, - const gchar *property_name, - const gchar *chooser_title); +static GtkWidget * gimp_prop_resource_chooser_factory (GimpResourceWidgetCreator widget_creator_func, + GObject *config, + const gchar *property_name, + const gchar *chooser_title); +static gchar * gimp_utils_make_canonical_menu_label (const gchar *path); /** @@ -146,6 +149,65 @@ gimp_prop_pattern_chooser_new (GObject *config, config, property_name, chooser_title); } +/** + * gimp_prop_drawable_chooser_new: + * @config: Object to which property is attached. + * @property_name: Name of a [class@Gimp.Drawable] property. + * @title: (nullable): title for the poppable dialog. + * + * Creates a [class@Gimp.DrawableChooser] controlled by the specified property. + * + * Returns: (transfer full): A new [class@Gimp.DrawableChooser]. + * + * Since: 3.0 + */ +GtkWidget * +gimp_prop_drawable_chooser_new (GObject *config, + const gchar *property_name, + const gchar *chooser_title) +{ + GParamSpec *param_spec; + GtkWidget *prop_chooser; + GimpDrawable *initial_drawable = 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_DRAWABLE), NULL); + + g_object_get (config, + property_name, &initial_drawable, + NULL); + + label = g_param_spec_get_nick (param_spec); + + if (chooser_title == NULL) + { + gchar *canonical; + + canonical = gimp_utils_make_canonical_menu_label (label); + title = g_strdup_printf (_("Choose drawable: %s"), canonical); + g_free (canonical); + } + else + { + title = g_strdup (chooser_title); + } + + prop_chooser = gimp_drawable_chooser_new (title, label, initial_drawable); + g_clear_object (&initial_drawable); + g_free (title); + + g_object_bind_property (prop_chooser, "drawable", + config, property_name, + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + + return prop_chooser; +} /*******************************/ /* private utility functions */ @@ -188,3 +250,22 @@ gimp_prop_resource_chooser_factory (GimpResourceWidgetCreator widget_creator_fu return prop_chooser; } + +/* This is a copy of the similarly-named function in app/widgets/gimpwidgets-utils.c + * I hesitated to put this maybe in libgimpwidgets/gimpwidgetsutils.h but for + * now, let's not. If it's useful to more people, it's always easier to move the + * function in rather than deprecating it. + */ +static gchar * +gimp_utils_make_canonical_menu_label (const gchar *path) +{ + gchar **split_path; + gchar *canon_path; + + /* The first underscore of each path item is a mnemonic. */ + split_path = g_strsplit (path, "_", 2); + canon_path = g_strjoinv ("", split_path); + g_strfreev (split_path); + + return canon_path; +} diff --git a/libgimp/gimppropwidgets.h b/libgimp/gimppropwidgets.h index bc663b2e83..0db7063158 100644 --- a/libgimp/gimppropwidgets.h +++ b/libgimp/gimppropwidgets.h @@ -48,6 +48,10 @@ GtkWidget * gimp_prop_pattern_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); + G_END_DECLS diff --git a/libgimp/gimpui.def b/libgimp/gimpui.def index 959eb9ccb5..e6aa6da9dc 100644 --- a/libgimp/gimpui.def +++ b/libgimp/gimpui.def @@ -5,6 +5,11 @@ EXPORTS gimp_brush_chooser_new gimp_channel_combo_box_get_type gimp_channel_combo_box_new + gimp_drawable_chooser_get_drawable + gimp_drawable_chooser_get_label + gimp_drawable_chooser_get_type + gimp_drawable_chooser_new + gimp_drawable_chooser_set_drawable gimp_drawable_combo_box_get_type gimp_drawable_combo_box_new gimp_drawable_preview_get_drawable @@ -59,6 +64,7 @@ EXPORTS gimp_progress_bar_get_type gimp_progress_bar_new gimp_prop_brush_chooser_new + gimp_prop_drawable_chooser_new gimp_prop_font_chooser_new gimp_prop_gradient_chooser_new gimp_prop_palette_chooser_new diff --git a/libgimp/gimpui.h b/libgimp/gimpui.h index 03fb060dcb..c09b2561cc 100644 --- a/libgimp/gimpui.h +++ b/libgimp/gimpui.h @@ -31,9 +31,9 @@ #include #include +#include #include #include -#include #include #include #include @@ -45,6 +45,7 @@ #include #include #include +#include #include #include diff --git a/libgimp/gimpuitypes.h b/libgimp/gimpuitypes.h index 5b519840dc..0ce78aedb9 100644 --- a/libgimp/gimpuitypes.h +++ b/libgimp/gimpuitypes.h @@ -43,6 +43,8 @@ typedef struct _GimpLayerComboBox GimpLayerComboBox; typedef struct _GimpVectorsComboBox GimpVectorsComboBox; typedef struct _GimpImageComboBox GimpImageComboBox; +typedef struct _GimpDrawableChooser GimpDrawableChooser; + typedef struct _GimpResourceChooser GimpResourceChooser; typedef struct _GimpBrushChooser GimpBrushChooser; typedef struct _GimpFontChooser GimpFontChooser; diff --git a/libgimp/meson.build b/libgimp/meson.build index f956b4fa46..746576555b 100644 --- a/libgimp/meson.build +++ b/libgimp/meson.build @@ -271,6 +271,7 @@ libgimp_headers = [ libgimpui_sources_introspectable = [ 'gimpaspectpreview.c', 'gimpbrushchooser.c', + 'gimpdrawablechooser.c', 'gimpdrawablepreview.c', 'gimpexport.c', 'gimpfontchooser.c', @@ -303,6 +304,7 @@ libgimpui_headers_introspectable = [ # Other headers 'gimpaspectpreview.h', 'gimpbrushchooser.h', + 'gimpdrawablechooser.h', 'gimpdrawablepreview.h', 'gimpexport.h', 'gimpfontchooser.h', diff --git a/po-libgimp/POTFILES.in b/po-libgimp/POTFILES.in index 59c1001d99..9c0b6bd3f1 100644 --- a/po-libgimp/POTFILES.in +++ b/po-libgimp/POTFILES.in @@ -2,18 +2,20 @@ # marked to allow runtime translation of messages libgimp/gimp.c -libgimp/gimpbrushselectbutton.c +libgimp/gimpbrushchooser.c +libgimp/gimpdrawablechooser.c libgimp/gimpexport.c libgimp/gimpimagemetadata.c libgimp/gimpimagemetadata-save.c libgimp/gimploadprocedure.c libgimp/gimpparamspecs-desc.c -libgimp/gimppatternselectbutton.c +libgimp/gimppatternchooser.c libgimp/gimppdb.c libgimp/gimpprocbrowserdialog.c libgimp/gimpprocedure.c libgimp/gimpproceduredialog.c libgimp/gimpprocview.c +libgimp/gimppropwidgets.c libgimp/gimpsaveprocedure.c libgimp/gimpsaveproceduredialog.c libgimp/gimpunitcache.c