diff options
Diffstat (limited to '')
-rw-r--r-- | app/widgets/gimpoverlaydialog.c | 643 |
1 files changed, 643 insertions, 0 deletions
diff --git a/app/widgets/gimpoverlaydialog.c b/app/widgets/gimpoverlaydialog.c new file mode 100644 index 0000000..4195323 --- /dev/null +++ b/app/widgets/gimpoverlaydialog.c @@ -0,0 +1,643 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpoverlaydialog.c + * Copyright (C) 2009-2010 Michael Natterer <mitch@gimp.org> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include "config.h" + +#include <gegl.h> +#include <gtk/gtk.h> +#include <gdk/gdkkeysyms.h> + +#include "libgimpwidgets/gimpwidgets.h" + +#include "widgets-types.h" + +#include "core/gimpmarshal.h" +#include "core/gimptoolinfo.h" + +#include "gimpoverlaydialog.h" + +#include "gimp-intl.h" + + +enum +{ + PROP_0, + PROP_TITLE, + PROP_ICON_NAME +}; + +enum +{ + RESPONSE, + DETACH, + CLOSE, + LAST_SIGNAL +}; + + +typedef struct _ResponseData ResponseData; + +struct _ResponseData +{ + gint response_id; +}; + + +static void gimp_overlay_dialog_constructed (GObject *object); +static void gimp_overlay_dialog_dispose (GObject *object); +static void gimp_overlay_dialog_finalize (GObject *object); +static void gimp_overlay_dialog_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); +static void gimp_overlay_dialog_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); + +static void gimp_overlay_dialog_size_request (GtkWidget *widget, + GtkRequisition *requisition); +static void gimp_overlay_dialog_size_allocate (GtkWidget *widget, + GtkAllocation *allocation); + +static void gimp_overlay_dialog_forall (GtkContainer *container, + gboolean include_internals, + GtkCallback callback, + gpointer callback_data); + +static void gimp_overlay_dialog_detach (GimpOverlayDialog *dialog); +static void gimp_overlay_dialog_real_detach (GimpOverlayDialog *dialog); + +static void gimp_overlay_dialog_close (GimpOverlayDialog *dialog); +static void gimp_overlay_dialog_real_close (GimpOverlayDialog *dialog); + +static ResponseData * get_response_data (GtkWidget *widget, + gboolean create); + + +G_DEFINE_TYPE (GimpOverlayDialog, gimp_overlay_dialog, + GIMP_TYPE_OVERLAY_FRAME) + +static guint signals[LAST_SIGNAL] = { 0, }; + +#define parent_class gimp_overlay_dialog_parent_class + + +static void +gimp_overlay_dialog_class_init (GimpOverlayDialogClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass); + + object_class->constructed = gimp_overlay_dialog_constructed; + object_class->dispose = gimp_overlay_dialog_dispose; + object_class->finalize = gimp_overlay_dialog_finalize; + object_class->get_property = gimp_overlay_dialog_get_property; + object_class->set_property = gimp_overlay_dialog_set_property; + + widget_class->size_request = gimp_overlay_dialog_size_request; + widget_class->size_allocate = gimp_overlay_dialog_size_allocate; + + container_class->forall = gimp_overlay_dialog_forall; + + klass->detach = gimp_overlay_dialog_real_detach; + klass->close = gimp_overlay_dialog_real_close; + + g_object_class_install_property (object_class, PROP_TITLE, + g_param_spec_string ("title", + NULL, NULL, + NULL, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_ICON_NAME, + g_param_spec_string ("icon-name", + NULL, NULL, + NULL, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + signals[RESPONSE] = + g_signal_new ("response", + G_OBJECT_CLASS_TYPE (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GimpOverlayDialogClass, response), + NULL, NULL, + gimp_marshal_VOID__INT, + G_TYPE_NONE, 1, + G_TYPE_INT); + + signals[DETACH] = + g_signal_new ("detach", + G_OBJECT_CLASS_TYPE (klass), + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, + G_STRUCT_OFFSET (GimpOverlayDialogClass, detach), + NULL, NULL, + gimp_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + signals[CLOSE] = + g_signal_new ("close", + G_OBJECT_CLASS_TYPE (klass), + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, + G_STRUCT_OFFSET (GimpOverlayDialogClass, close), + NULL, NULL, + gimp_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + gtk_binding_entry_add_signal (gtk_binding_set_by_class (klass), + GDK_KEY_Escape, 0, "close", 0); +} + +static void +gimp_overlay_dialog_init (GimpOverlayDialog *dialog) +{ + dialog->header = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 4); + gtk_widget_set_parent (dialog->header, GTK_WIDGET (dialog)); + gtk_widget_show (dialog->header); + + dialog->action_area = gtk_button_box_new (GTK_ORIENTATION_HORIZONTAL); + gtk_button_box_set_layout (GTK_BUTTON_BOX (dialog->action_area), + GTK_BUTTONBOX_END); + gtk_widget_set_parent (dialog->action_area, GTK_WIDGET (dialog)); + gtk_widget_show (dialog->action_area); +} + +static void +gimp_overlay_dialog_constructed (GObject *object) +{ + GimpOverlayDialog *dialog = GIMP_OVERLAY_DIALOG (object); + GtkWidget *label; + GtkWidget *button; + GtkWidget *image; + + G_OBJECT_CLASS (parent_class)->constructed (object); + + dialog->icon_image = image = gtk_image_new_from_icon_name (dialog->icon_name, + GTK_ICON_SIZE_MENU); + gtk_box_pack_start (GTK_BOX (dialog->header), image, FALSE, FALSE, 0); + gtk_widget_show (image); + + dialog->title_label = label = gtk_label_new (dialog->title); + gimp_label_set_attributes (GTK_LABEL (label), + PANGO_ATTR_WEIGHT, PANGO_WEIGHT_BOLD, + -1); + gtk_box_pack_start (GTK_BOX (dialog->header), label, TRUE, TRUE, 0); + gtk_widget_show (label); + + dialog->close_button = button = gtk_button_new (); + gtk_widget_set_can_focus (button, FALSE); + gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE); + gtk_box_pack_end (GTK_BOX (dialog->header), button, FALSE, FALSE, 0); + gtk_widget_show (button); + + image = gtk_image_new_from_icon_name (GIMP_ICON_CLOSE, GTK_ICON_SIZE_MENU); + gtk_image_set_pixel_size (GTK_IMAGE (image), 12); + gtk_container_add (GTK_CONTAINER (button), image); + gtk_widget_show (image); + + g_signal_connect_object (button, "clicked", + G_CALLBACK (gimp_overlay_dialog_close), + G_OBJECT (dialog), + G_CONNECT_SWAPPED); + + dialog->detach_button = button = gtk_button_new (); + gtk_widget_set_can_focus (button, FALSE); + gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE); + gtk_box_pack_end (GTK_BOX (dialog->header), button, FALSE, FALSE, 0); + gtk_widget_show (button); + + gimp_help_set_help_data (dialog->detach_button, + _("Detach dialog from canvas"), NULL); + + image = gtk_image_new_from_icon_name (GIMP_ICON_DETACH, + GTK_ICON_SIZE_MENU); + gtk_image_set_pixel_size (GTK_IMAGE (image), 12); + gtk_container_add (GTK_CONTAINER (button), image); + gtk_widget_show (image); + + g_signal_connect_object (button, "clicked", + G_CALLBACK (gimp_overlay_dialog_detach), + G_OBJECT (dialog), + G_CONNECT_SWAPPED); +} + +static void +gimp_overlay_dialog_dispose (GObject *object) +{ + GimpOverlayDialog *dialog = GIMP_OVERLAY_DIALOG (object); + + if (dialog->header) + { + gtk_widget_unparent (dialog->header); + dialog->header = NULL; + } + + if (dialog->action_area) + { + gtk_widget_unparent (dialog->action_area); + dialog->action_area = NULL; + } + + G_OBJECT_CLASS (parent_class)->dispose (object); +} + +static void +gimp_overlay_dialog_finalize (GObject *object) +{ + GimpOverlayDialog *dialog = GIMP_OVERLAY_DIALOG (object); + + g_clear_pointer (&dialog->title, g_free); + g_clear_pointer (&dialog->icon_name, g_free); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +gimp_overlay_dialog_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpOverlayDialog *dialog = GIMP_OVERLAY_DIALOG (object); + + switch (property_id) + { + case PROP_TITLE: + g_free (dialog->title); + dialog->title = g_value_dup_string (value); + if (dialog->title_label) + gtk_label_set_text (GTK_LABEL (dialog->title_label), dialog->title); + break; + + case PROP_ICON_NAME: + g_free (dialog->icon_name); + dialog->icon_name = g_value_dup_string (value); + if (dialog->icon_image) + gtk_image_set_from_icon_name (GTK_IMAGE (dialog->icon_image), + dialog->icon_name, GTK_ICON_SIZE_MENU); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_overlay_dialog_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpOverlayDialog *dialog = GIMP_OVERLAY_DIALOG (object); + + switch (property_id) + { + case PROP_TITLE: + g_value_set_string (value, dialog->title); + break; + + case PROP_ICON_NAME: + g_value_set_string (value, dialog->icon_name); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_overlay_dialog_size_request (GtkWidget *widget, + GtkRequisition *requisition) +{ + GtkContainer *container = GTK_CONTAINER (widget); + GimpOverlayDialog *dialog = GIMP_OVERLAY_DIALOG (widget); + GtkWidget *child = gtk_bin_get_child (GTK_BIN (widget)); + GtkRequisition child_requisition; + GtkRequisition header_requisition; + GtkRequisition action_requisition; + gint border_width; + + border_width = gtk_container_get_border_width (container); + + requisition->width = border_width * 2; + requisition->height = border_width * 2; + + if (child && gtk_widget_get_visible (child)) + { + gtk_widget_size_request (child, &child_requisition); + } + else + { + child_requisition.width = 0; + child_requisition.height = 0; + } + + gtk_widget_size_request (dialog->header, &header_requisition); + gtk_widget_size_request (dialog->action_area, &action_requisition); + + requisition->width += MAX (MAX (child_requisition.width, + action_requisition.width), + header_requisition.width); + requisition->height += (child_requisition.height + + 2 * border_width + + header_requisition.height + + action_requisition.height); +} + +static void +gimp_overlay_dialog_size_allocate (GtkWidget *widget, + GtkAllocation *allocation) +{ + GtkContainer *container = GTK_CONTAINER (widget); + GimpOverlayDialog *dialog = GIMP_OVERLAY_DIALOG (widget); + GtkWidget *child = gtk_bin_get_child (GTK_BIN (widget)); + GtkRequisition header_requisition; + GtkRequisition action_requisition; + GtkAllocation child_allocation = { 0, }; + GtkAllocation header_allocation; + GtkAllocation action_allocation; + gint border_width; + + gtk_widget_set_allocation (widget, allocation); + + border_width = gtk_container_get_border_width (container); + + gtk_widget_size_request (dialog->header, &header_requisition); + gtk_widget_size_request (dialog->action_area, &action_requisition); + + if (child && gtk_widget_get_visible (child)) + { + child_allocation.x = allocation->x + border_width; + child_allocation.y = (allocation->y + 2 * border_width + + header_requisition.height); + child_allocation.width = MAX (allocation->width - 2 * border_width, 0); + child_allocation.height = MAX (allocation->height - + 4 * border_width - + header_requisition.height - + action_requisition.height, 0); + + gtk_widget_size_allocate (child, &child_allocation); + } + + header_allocation.x = allocation->x + border_width; + header_allocation.y = allocation->y + border_width; + header_allocation.width = MAX (allocation->width - 2 * border_width, 0); + header_allocation.height = header_requisition.height; + + gtk_widget_size_allocate (dialog->header, &header_allocation); + + action_allocation.x = allocation->x + border_width; + action_allocation.y = (child_allocation.y + child_allocation.height + + border_width); + action_allocation.width = MAX (allocation->width - 2 * border_width, 0); + action_allocation.height = action_requisition.height; + + gtk_widget_size_allocate (dialog->action_area, &action_allocation); +} + +static void +gimp_overlay_dialog_forall (GtkContainer *container, + gboolean include_internals, + GtkCallback callback, + gpointer callback_data) +{ + GTK_CONTAINER_CLASS (parent_class)->forall (container, include_internals, + callback, callback_data); + + if (include_internals) + { + GimpOverlayDialog *dialog = GIMP_OVERLAY_DIALOG (container); + + if (dialog->header) + (* callback) (dialog->header, callback_data); + + if (dialog->action_area) + (* callback) (dialog->action_area, callback_data); + } +} + +static void +gimp_overlay_dialog_detach (GimpOverlayDialog *dialog) +{ + g_signal_emit (dialog, signals[DETACH], 0); +} + +static void +gimp_overlay_dialog_real_detach (GimpOverlayDialog *dialog) +{ + gimp_overlay_dialog_response (dialog, GIMP_RESPONSE_DETACH); +} + +static void +gimp_overlay_dialog_close (GimpOverlayDialog *dialog) +{ + g_signal_emit (dialog, signals[CLOSE], 0); +} + +static void +gimp_overlay_dialog_real_close (GimpOverlayDialog *dialog) +{ + gimp_overlay_dialog_response (dialog, GTK_RESPONSE_DELETE_EVENT); +} + +GtkWidget * +gimp_overlay_dialog_new (GimpToolInfo *tool_info, + const gchar *desc, + ...) +{ + GimpOverlayDialog *dialog; + const gchar *icon_name; + va_list args; + + g_return_val_if_fail (GIMP_IS_TOOL_INFO (tool_info), NULL); + + icon_name = gimp_viewable_get_icon_name (GIMP_VIEWABLE (tool_info)); + + dialog = g_object_new (GIMP_TYPE_OVERLAY_DIALOG, + "title", tool_info->label, + "icon-name", icon_name, + NULL); + + va_start (args, desc); + gimp_overlay_dialog_add_buttons_valist (dialog, args); + va_end (args); + + return GTK_WIDGET (dialog); +} + +void +gimp_overlay_dialog_response (GimpOverlayDialog *dialog, + gint response_id) +{ + g_return_if_fail (GIMP_IS_OVERLAY_DIALOG (dialog)); + + g_signal_emit (dialog, signals[RESPONSE], 0, + response_id); +} + +void +gimp_overlay_dialog_add_buttons_valist (GimpOverlayDialog *dialog, + va_list args) +{ + const gchar *button_text; + gint response_id; + + g_return_if_fail (GIMP_IS_OVERLAY_DIALOG (dialog)); + + while ((button_text = va_arg (args, const gchar *))) + { + response_id = va_arg (args, gint); + + gimp_overlay_dialog_add_button (dialog, button_text, response_id); + } +} + +static void +action_widget_activated (GtkWidget *widget, + GimpOverlayDialog *dialog) +{ + ResponseData *ad = get_response_data (widget, FALSE); + + gimp_overlay_dialog_response (dialog, ad->response_id); +} + +GtkWidget * +gimp_overlay_dialog_add_button (GimpOverlayDialog *dialog, + const gchar *button_text, + gint response_id) +{ + GtkWidget *button; + ResponseData *ad; + guint signal_id; + GClosure *closure; + + g_return_val_if_fail (GIMP_IS_OVERLAY_DIALOG (dialog), NULL); + g_return_val_if_fail (button_text != NULL, NULL); + + if (response_id == GTK_RESPONSE_CANCEL || + response_id == GTK_RESPONSE_CLOSE || + response_id == GIMP_RESPONSE_DETACH) + return NULL; + + button = gtk_button_new_with_mnemonic (button_text); + gtk_widget_set_can_default (button, TRUE); + gtk_widget_show (button); + + ad = get_response_data (button, TRUE); + + ad->response_id = response_id; + + signal_id = g_signal_lookup ("clicked", GTK_TYPE_BUTTON); + + closure = g_cclosure_new_object (G_CALLBACK (action_widget_activated), + G_OBJECT (dialog)); + g_signal_connect_closure_by_id (button, signal_id, 0, + closure, FALSE); + + gtk_box_pack_end (GTK_BOX (dialog->action_area), button, FALSE, TRUE, 0); + + if (response_id == GTK_RESPONSE_HELP) + gtk_button_box_set_child_secondary (GTK_BUTTON_BOX (dialog->action_area), + button, TRUE); + + return button; +} + +void +gimp_overlay_dialog_set_alternative_button_order (GimpOverlayDialog *overlay, + gint n_ids, + gint *ids) +{ + /* TODO */ +} + +void +gimp_overlay_dialog_set_default_response (GimpOverlayDialog *overlay, + gint response_id) +{ + /* TODO */ +} + +void +gimp_overlay_dialog_set_response_sensitive (GimpOverlayDialog *overlay, + gint response_id, + gboolean sensitive) +{ + GList *children; + GList *list; + + g_return_if_fail (GIMP_IS_OVERLAY_DIALOG (overlay)); + + if (response_id == GTK_RESPONSE_CANCEL || + response_id == GTK_RESPONSE_CLOSE) + { + gtk_widget_set_sensitive (overlay->close_button, sensitive); + } + + if (response_id == GIMP_RESPONSE_DETACH) + { + gtk_widget_set_sensitive (overlay->detach_button, sensitive); + } + + children = gtk_container_get_children (GTK_CONTAINER (overlay->action_area)); + + for (list = children; list; list = g_list_next (list)) + { + GtkWidget *child = list->data; + ResponseData *ad = get_response_data (child, FALSE); + + if (ad && ad->response_id == response_id) + { + gtk_widget_set_sensitive (child, sensitive); + break; + } + } + + g_list_free (children); +} + +static void +response_data_free (gpointer data) +{ + g_slice_free (ResponseData, data); +} + +static ResponseData * +get_response_data (GtkWidget *widget, + gboolean create) +{ + ResponseData *ad = g_object_get_data (G_OBJECT (widget), + "gimp-overlay-dialog-response-data"); + + if (! ad && create) + { + ad = g_slice_new (ResponseData); + + g_object_set_data_full (G_OBJECT (widget), + "gimp-overlay-dialog-response-data", + ad, response_data_free); + } + + return ad; +} |