1 /* GIMP - The GNU Image Manipulation Program
2 * Copyright (C) 1995 Spencer Kimball and Peter Mattis
3 *
4 * gimpoverlaydialog.c
5 * Copyright (C) 2009-2010 Michael Natterer <mitch@gimp.org>
6 *
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <https://www.gnu.org/licenses/>.
19 */
20
21 #include "config.h"
22
23 #include <gegl.h>
24 #include <gtk/gtk.h>
25 #include <gdk/gdkkeysyms.h>
26
27 #include "libgimpwidgets/gimpwidgets.h"
28
29 #include "widgets-types.h"
30
31 #include "core/gimpmarshal.h"
32 #include "core/gimptoolinfo.h"
33
34 #include "gimpoverlaydialog.h"
35
36 #include "gimp-intl.h"
37
38
39 enum
40 {
41 PROP_0,
42 PROP_TITLE,
43 PROP_ICON_NAME
44 };
45
46 enum
47 {
48 RESPONSE,
49 DETACH,
50 CLOSE,
51 LAST_SIGNAL
52 };
53
54
55 typedef struct _ResponseData ResponseData;
56
57 struct _ResponseData
58 {
59 gint response_id;
60 };
61
62
63 static void gimp_overlay_dialog_constructed (GObject *object);
64 static void gimp_overlay_dialog_dispose (GObject *object);
65 static void gimp_overlay_dialog_finalize (GObject *object);
66 static void gimp_overlay_dialog_set_property (GObject *object,
67 guint property_id,
68 const GValue *value,
69 GParamSpec *pspec);
70 static void gimp_overlay_dialog_get_property (GObject *object,
71 guint property_id,
72 GValue *value,
73 GParamSpec *pspec);
74
75 static void gimp_overlay_dialog_size_request (GtkWidget *widget,
76 GtkRequisition *requisition);
77 static void gimp_overlay_dialog_size_allocate (GtkWidget *widget,
78 GtkAllocation *allocation);
79
80 static void gimp_overlay_dialog_forall (GtkContainer *container,
81 gboolean include_internals,
82 GtkCallback callback,
83 gpointer callback_data);
84
85 static void gimp_overlay_dialog_detach (GimpOverlayDialog *dialog);
86 static void gimp_overlay_dialog_real_detach (GimpOverlayDialog *dialog);
87
88 static void gimp_overlay_dialog_close (GimpOverlayDialog *dialog);
89 static void gimp_overlay_dialog_real_close (GimpOverlayDialog *dialog);
90
91 static ResponseData * get_response_data (GtkWidget *widget,
92 gboolean create);
93
94
95 G_DEFINE_TYPE (GimpOverlayDialog, gimp_overlay_dialog,
96 GIMP_TYPE_OVERLAY_FRAME)
97
98 static guint signals[LAST_SIGNAL] = { 0, };
99
100 #define parent_class gimp_overlay_dialog_parent_class
101
102
103 static void
gimp_overlay_dialog_class_init(GimpOverlayDialogClass * klass)104 gimp_overlay_dialog_class_init (GimpOverlayDialogClass *klass)
105 {
106 GObjectClass *object_class = G_OBJECT_CLASS (klass);
107 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
108 GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
109
110 object_class->constructed = gimp_overlay_dialog_constructed;
111 object_class->dispose = gimp_overlay_dialog_dispose;
112 object_class->finalize = gimp_overlay_dialog_finalize;
113 object_class->get_property = gimp_overlay_dialog_get_property;
114 object_class->set_property = gimp_overlay_dialog_set_property;
115
116 widget_class->size_request = gimp_overlay_dialog_size_request;
117 widget_class->size_allocate = gimp_overlay_dialog_size_allocate;
118
119 container_class->forall = gimp_overlay_dialog_forall;
120
121 klass->detach = gimp_overlay_dialog_real_detach;
122 klass->close = gimp_overlay_dialog_real_close;
123
124 g_object_class_install_property (object_class, PROP_TITLE,
125 g_param_spec_string ("title",
126 NULL, NULL,
127 NULL,
128 GIMP_PARAM_READWRITE |
129 G_PARAM_CONSTRUCT));
130
131 g_object_class_install_property (object_class, PROP_ICON_NAME,
132 g_param_spec_string ("icon-name",
133 NULL, NULL,
134 NULL,
135 GIMP_PARAM_READWRITE |
136 G_PARAM_CONSTRUCT));
137
138 signals[RESPONSE] =
139 g_signal_new ("response",
140 G_OBJECT_CLASS_TYPE (klass),
141 G_SIGNAL_RUN_LAST,
142 G_STRUCT_OFFSET (GimpOverlayDialogClass, response),
143 NULL, NULL,
144 gimp_marshal_VOID__INT,
145 G_TYPE_NONE, 1,
146 G_TYPE_INT);
147
148 signals[DETACH] =
149 g_signal_new ("detach",
150 G_OBJECT_CLASS_TYPE (klass),
151 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
152 G_STRUCT_OFFSET (GimpOverlayDialogClass, detach),
153 NULL, NULL,
154 gimp_marshal_VOID__VOID,
155 G_TYPE_NONE, 0);
156
157 signals[CLOSE] =
158 g_signal_new ("close",
159 G_OBJECT_CLASS_TYPE (klass),
160 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
161 G_STRUCT_OFFSET (GimpOverlayDialogClass, close),
162 NULL, NULL,
163 gimp_marshal_VOID__VOID,
164 G_TYPE_NONE, 0);
165
166 gtk_binding_entry_add_signal (gtk_binding_set_by_class (klass),
167 GDK_KEY_Escape, 0, "close", 0);
168 }
169
170 static void
gimp_overlay_dialog_init(GimpOverlayDialog * dialog)171 gimp_overlay_dialog_init (GimpOverlayDialog *dialog)
172 {
173 dialog->header = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 4);
174 gtk_widget_set_parent (dialog->header, GTK_WIDGET (dialog));
175 gtk_widget_show (dialog->header);
176
177 dialog->action_area = gtk_button_box_new (GTK_ORIENTATION_HORIZONTAL);
178 gtk_button_box_set_layout (GTK_BUTTON_BOX (dialog->action_area),
179 GTK_BUTTONBOX_END);
180 gtk_widget_set_parent (dialog->action_area, GTK_WIDGET (dialog));
181 gtk_widget_show (dialog->action_area);
182 }
183
184 static void
gimp_overlay_dialog_constructed(GObject * object)185 gimp_overlay_dialog_constructed (GObject *object)
186 {
187 GimpOverlayDialog *dialog = GIMP_OVERLAY_DIALOG (object);
188 GtkWidget *label;
189 GtkWidget *button;
190 GtkWidget *image;
191
192 G_OBJECT_CLASS (parent_class)->constructed (object);
193
194 dialog->icon_image = image = gtk_image_new_from_icon_name (dialog->icon_name,
195 GTK_ICON_SIZE_MENU);
196 gtk_box_pack_start (GTK_BOX (dialog->header), image, FALSE, FALSE, 0);
197 gtk_widget_show (image);
198
199 dialog->title_label = label = gtk_label_new (dialog->title);
200 gimp_label_set_attributes (GTK_LABEL (label),
201 PANGO_ATTR_WEIGHT, PANGO_WEIGHT_BOLD,
202 -1);
203 gtk_box_pack_start (GTK_BOX (dialog->header), label, TRUE, TRUE, 0);
204 gtk_widget_show (label);
205
206 dialog->close_button = button = gtk_button_new ();
207 gtk_widget_set_can_focus (button, FALSE);
208 gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE);
209 gtk_box_pack_end (GTK_BOX (dialog->header), button, FALSE, FALSE, 0);
210 gtk_widget_show (button);
211
212 image = gtk_image_new_from_icon_name (GIMP_ICON_CLOSE, GTK_ICON_SIZE_MENU);
213 gtk_image_set_pixel_size (GTK_IMAGE (image), 12);
214 gtk_container_add (GTK_CONTAINER (button), image);
215 gtk_widget_show (image);
216
217 g_signal_connect_object (button, "clicked",
218 G_CALLBACK (gimp_overlay_dialog_close),
219 G_OBJECT (dialog),
220 G_CONNECT_SWAPPED);
221
222 dialog->detach_button = button = gtk_button_new ();
223 gtk_widget_set_can_focus (button, FALSE);
224 gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE);
225 gtk_box_pack_end (GTK_BOX (dialog->header), button, FALSE, FALSE, 0);
226 gtk_widget_show (button);
227
228 gimp_help_set_help_data (dialog->detach_button,
229 _("Detach dialog from canvas"), NULL);
230
231 image = gtk_image_new_from_icon_name (GIMP_ICON_DETACH,
232 GTK_ICON_SIZE_MENU);
233 gtk_image_set_pixel_size (GTK_IMAGE (image), 12);
234 gtk_container_add (GTK_CONTAINER (button), image);
235 gtk_widget_show (image);
236
237 g_signal_connect_object (button, "clicked",
238 G_CALLBACK (gimp_overlay_dialog_detach),
239 G_OBJECT (dialog),
240 G_CONNECT_SWAPPED);
241 }
242
243 static void
gimp_overlay_dialog_dispose(GObject * object)244 gimp_overlay_dialog_dispose (GObject *object)
245 {
246 GimpOverlayDialog *dialog = GIMP_OVERLAY_DIALOG (object);
247
248 if (dialog->header)
249 {
250 gtk_widget_unparent (dialog->header);
251 dialog->header = NULL;
252 }
253
254 if (dialog->action_area)
255 {
256 gtk_widget_unparent (dialog->action_area);
257 dialog->action_area = NULL;
258 }
259
260 G_OBJECT_CLASS (parent_class)->dispose (object);
261 }
262
263 static void
gimp_overlay_dialog_finalize(GObject * object)264 gimp_overlay_dialog_finalize (GObject *object)
265 {
266 GimpOverlayDialog *dialog = GIMP_OVERLAY_DIALOG (object);
267
268 g_clear_pointer (&dialog->title, g_free);
269 g_clear_pointer (&dialog->icon_name, g_free);
270
271 G_OBJECT_CLASS (parent_class)->finalize (object);
272 }
273
274 static void
gimp_overlay_dialog_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)275 gimp_overlay_dialog_set_property (GObject *object,
276 guint property_id,
277 const GValue *value,
278 GParamSpec *pspec)
279 {
280 GimpOverlayDialog *dialog = GIMP_OVERLAY_DIALOG (object);
281
282 switch (property_id)
283 {
284 case PROP_TITLE:
285 g_free (dialog->title);
286 dialog->title = g_value_dup_string (value);
287 if (dialog->title_label)
288 gtk_label_set_text (GTK_LABEL (dialog->title_label), dialog->title);
289 break;
290
291 case PROP_ICON_NAME:
292 g_free (dialog->icon_name);
293 dialog->icon_name = g_value_dup_string (value);
294 if (dialog->icon_image)
295 gtk_image_set_from_icon_name (GTK_IMAGE (dialog->icon_image),
296 dialog->icon_name, GTK_ICON_SIZE_MENU);
297 break;
298
299 default:
300 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
301 break;
302 }
303 }
304
305 static void
gimp_overlay_dialog_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)306 gimp_overlay_dialog_get_property (GObject *object,
307 guint property_id,
308 GValue *value,
309 GParamSpec *pspec)
310 {
311 GimpOverlayDialog *dialog = GIMP_OVERLAY_DIALOG (object);
312
313 switch (property_id)
314 {
315 case PROP_TITLE:
316 g_value_set_string (value, dialog->title);
317 break;
318
319 case PROP_ICON_NAME:
320 g_value_set_string (value, dialog->icon_name);
321 break;
322
323 default:
324 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
325 break;
326 }
327 }
328
329 static void
gimp_overlay_dialog_size_request(GtkWidget * widget,GtkRequisition * requisition)330 gimp_overlay_dialog_size_request (GtkWidget *widget,
331 GtkRequisition *requisition)
332 {
333 GtkContainer *container = GTK_CONTAINER (widget);
334 GimpOverlayDialog *dialog = GIMP_OVERLAY_DIALOG (widget);
335 GtkWidget *child = gtk_bin_get_child (GTK_BIN (widget));
336 GtkRequisition child_requisition;
337 GtkRequisition header_requisition;
338 GtkRequisition action_requisition;
339 gint border_width;
340
341 border_width = gtk_container_get_border_width (container);
342
343 requisition->width = border_width * 2;
344 requisition->height = border_width * 2;
345
346 if (child && gtk_widget_get_visible (child))
347 {
348 gtk_widget_size_request (child, &child_requisition);
349 }
350 else
351 {
352 child_requisition.width = 0;
353 child_requisition.height = 0;
354 }
355
356 gtk_widget_size_request (dialog->header, &header_requisition);
357 gtk_widget_size_request (dialog->action_area, &action_requisition);
358
359 requisition->width += MAX (MAX (child_requisition.width,
360 action_requisition.width),
361 header_requisition.width);
362 requisition->height += (child_requisition.height +
363 2 * border_width +
364 header_requisition.height +
365 action_requisition.height);
366 }
367
368 static void
gimp_overlay_dialog_size_allocate(GtkWidget * widget,GtkAllocation * allocation)369 gimp_overlay_dialog_size_allocate (GtkWidget *widget,
370 GtkAllocation *allocation)
371 {
372 GtkContainer *container = GTK_CONTAINER (widget);
373 GimpOverlayDialog *dialog = GIMP_OVERLAY_DIALOG (widget);
374 GtkWidget *child = gtk_bin_get_child (GTK_BIN (widget));
375 GtkRequisition header_requisition;
376 GtkRequisition action_requisition;
377 GtkAllocation child_allocation = { 0, };
378 GtkAllocation header_allocation;
379 GtkAllocation action_allocation;
380 gint border_width;
381
382 gtk_widget_set_allocation (widget, allocation);
383
384 border_width = gtk_container_get_border_width (container);
385
386 gtk_widget_size_request (dialog->header, &header_requisition);
387 gtk_widget_size_request (dialog->action_area, &action_requisition);
388
389 if (child && gtk_widget_get_visible (child))
390 {
391 child_allocation.x = allocation->x + border_width;
392 child_allocation.y = (allocation->y + 2 * border_width +
393 header_requisition.height);
394 child_allocation.width = MAX (allocation->width - 2 * border_width, 0);
395 child_allocation.height = MAX (allocation->height -
396 4 * border_width -
397 header_requisition.height -
398 action_requisition.height, 0);
399
400 gtk_widget_size_allocate (child, &child_allocation);
401 }
402
403 header_allocation.x = allocation->x + border_width;
404 header_allocation.y = allocation->y + border_width;
405 header_allocation.width = MAX (allocation->width - 2 * border_width, 0);
406 header_allocation.height = header_requisition.height;
407
408 gtk_widget_size_allocate (dialog->header, &header_allocation);
409
410 action_allocation.x = allocation->x + border_width;
411 action_allocation.y = (child_allocation.y + child_allocation.height +
412 border_width);
413 action_allocation.width = MAX (allocation->width - 2 * border_width, 0);
414 action_allocation.height = action_requisition.height;
415
416 gtk_widget_size_allocate (dialog->action_area, &action_allocation);
417 }
418
419 static void
gimp_overlay_dialog_forall(GtkContainer * container,gboolean include_internals,GtkCallback callback,gpointer callback_data)420 gimp_overlay_dialog_forall (GtkContainer *container,
421 gboolean include_internals,
422 GtkCallback callback,
423 gpointer callback_data)
424 {
425 GTK_CONTAINER_CLASS (parent_class)->forall (container, include_internals,
426 callback, callback_data);
427
428 if (include_internals)
429 {
430 GimpOverlayDialog *dialog = GIMP_OVERLAY_DIALOG (container);
431
432 if (dialog->header)
433 (* callback) (dialog->header, callback_data);
434
435 if (dialog->action_area)
436 (* callback) (dialog->action_area, callback_data);
437 }
438 }
439
440 static void
gimp_overlay_dialog_detach(GimpOverlayDialog * dialog)441 gimp_overlay_dialog_detach (GimpOverlayDialog *dialog)
442 {
443 g_signal_emit (dialog, signals[DETACH], 0);
444 }
445
446 static void
gimp_overlay_dialog_real_detach(GimpOverlayDialog * dialog)447 gimp_overlay_dialog_real_detach (GimpOverlayDialog *dialog)
448 {
449 gimp_overlay_dialog_response (dialog, GIMP_RESPONSE_DETACH);
450 }
451
452 static void
gimp_overlay_dialog_close(GimpOverlayDialog * dialog)453 gimp_overlay_dialog_close (GimpOverlayDialog *dialog)
454 {
455 g_signal_emit (dialog, signals[CLOSE], 0);
456 }
457
458 static void
gimp_overlay_dialog_real_close(GimpOverlayDialog * dialog)459 gimp_overlay_dialog_real_close (GimpOverlayDialog *dialog)
460 {
461 gimp_overlay_dialog_response (dialog, GTK_RESPONSE_DELETE_EVENT);
462 }
463
464 GtkWidget *
gimp_overlay_dialog_new(GimpToolInfo * tool_info,const gchar * desc,...)465 gimp_overlay_dialog_new (GimpToolInfo *tool_info,
466 const gchar *desc,
467 ...)
468 {
469 GimpOverlayDialog *dialog;
470 const gchar *icon_name;
471 va_list args;
472
473 g_return_val_if_fail (GIMP_IS_TOOL_INFO (tool_info), NULL);
474
475 icon_name = gimp_viewable_get_icon_name (GIMP_VIEWABLE (tool_info));
476
477 dialog = g_object_new (GIMP_TYPE_OVERLAY_DIALOG,
478 "title", tool_info->label,
479 "icon-name", icon_name,
480 NULL);
481
482 va_start (args, desc);
483 gimp_overlay_dialog_add_buttons_valist (dialog, args);
484 va_end (args);
485
486 return GTK_WIDGET (dialog);
487 }
488
489 void
gimp_overlay_dialog_response(GimpOverlayDialog * dialog,gint response_id)490 gimp_overlay_dialog_response (GimpOverlayDialog *dialog,
491 gint response_id)
492 {
493 g_return_if_fail (GIMP_IS_OVERLAY_DIALOG (dialog));
494
495 g_signal_emit (dialog, signals[RESPONSE], 0,
496 response_id);
497 }
498
499 void
gimp_overlay_dialog_add_buttons_valist(GimpOverlayDialog * dialog,va_list args)500 gimp_overlay_dialog_add_buttons_valist (GimpOverlayDialog *dialog,
501 va_list args)
502 {
503 const gchar *button_text;
504 gint response_id;
505
506 g_return_if_fail (GIMP_IS_OVERLAY_DIALOG (dialog));
507
508 while ((button_text = va_arg (args, const gchar *)))
509 {
510 response_id = va_arg (args, gint);
511
512 gimp_overlay_dialog_add_button (dialog, button_text, response_id);
513 }
514 }
515
516 static void
action_widget_activated(GtkWidget * widget,GimpOverlayDialog * dialog)517 action_widget_activated (GtkWidget *widget,
518 GimpOverlayDialog *dialog)
519 {
520 ResponseData *ad = get_response_data (widget, FALSE);
521
522 gimp_overlay_dialog_response (dialog, ad->response_id);
523 }
524
525 GtkWidget *
gimp_overlay_dialog_add_button(GimpOverlayDialog * dialog,const gchar * button_text,gint response_id)526 gimp_overlay_dialog_add_button (GimpOverlayDialog *dialog,
527 const gchar *button_text,
528 gint response_id)
529 {
530 GtkWidget *button;
531 ResponseData *ad;
532 guint signal_id;
533 GClosure *closure;
534
535 g_return_val_if_fail (GIMP_IS_OVERLAY_DIALOG (dialog), NULL);
536 g_return_val_if_fail (button_text != NULL, NULL);
537
538 if (response_id == GTK_RESPONSE_CANCEL ||
539 response_id == GTK_RESPONSE_CLOSE ||
540 response_id == GIMP_RESPONSE_DETACH)
541 return NULL;
542
543 button = gtk_button_new_with_mnemonic (button_text);
544 gtk_widget_set_can_default (button, TRUE);
545 gtk_widget_show (button);
546
547 ad = get_response_data (button, TRUE);
548
549 ad->response_id = response_id;
550
551 signal_id = g_signal_lookup ("clicked", GTK_TYPE_BUTTON);
552
553 closure = g_cclosure_new_object (G_CALLBACK (action_widget_activated),
554 G_OBJECT (dialog));
555 g_signal_connect_closure_by_id (button, signal_id, 0,
556 closure, FALSE);
557
558 gtk_box_pack_end (GTK_BOX (dialog->action_area), button, FALSE, TRUE, 0);
559
560 if (response_id == GTK_RESPONSE_HELP)
561 gtk_button_box_set_child_secondary (GTK_BUTTON_BOX (dialog->action_area),
562 button, TRUE);
563
564 return button;
565 }
566
567 void
gimp_overlay_dialog_set_alternative_button_order(GimpOverlayDialog * overlay,gint n_ids,gint * ids)568 gimp_overlay_dialog_set_alternative_button_order (GimpOverlayDialog *overlay,
569 gint n_ids,
570 gint *ids)
571 {
572 /* TODO */
573 }
574
575 void
gimp_overlay_dialog_set_default_response(GimpOverlayDialog * overlay,gint response_id)576 gimp_overlay_dialog_set_default_response (GimpOverlayDialog *overlay,
577 gint response_id)
578 {
579 /* TODO */
580 }
581
582 void
gimp_overlay_dialog_set_response_sensitive(GimpOverlayDialog * overlay,gint response_id,gboolean sensitive)583 gimp_overlay_dialog_set_response_sensitive (GimpOverlayDialog *overlay,
584 gint response_id,
585 gboolean sensitive)
586 {
587 GList *children;
588 GList *list;
589
590 g_return_if_fail (GIMP_IS_OVERLAY_DIALOG (overlay));
591
592 if (response_id == GTK_RESPONSE_CANCEL ||
593 response_id == GTK_RESPONSE_CLOSE)
594 {
595 gtk_widget_set_sensitive (overlay->close_button, sensitive);
596 }
597
598 if (response_id == GIMP_RESPONSE_DETACH)
599 {
600 gtk_widget_set_sensitive (overlay->detach_button, sensitive);
601 }
602
603 children = gtk_container_get_children (GTK_CONTAINER (overlay->action_area));
604
605 for (list = children; list; list = g_list_next (list))
606 {
607 GtkWidget *child = list->data;
608 ResponseData *ad = get_response_data (child, FALSE);
609
610 if (ad && ad->response_id == response_id)
611 {
612 gtk_widget_set_sensitive (child, sensitive);
613 break;
614 }
615 }
616
617 g_list_free (children);
618 }
619
620 static void
response_data_free(gpointer data)621 response_data_free (gpointer data)
622 {
623 g_slice_free (ResponseData, data);
624 }
625
626 static ResponseData *
get_response_data(GtkWidget * widget,gboolean create)627 get_response_data (GtkWidget *widget,
628 gboolean create)
629 {
630 ResponseData *ad = g_object_get_data (G_OBJECT (widget),
631 "gimp-overlay-dialog-response-data");
632
633 if (! ad && create)
634 {
635 ad = g_slice_new (ResponseData);
636
637 g_object_set_data_full (G_OBJECT (widget),
638 "gimp-overlay-dialog-response-data",
639 ad, response_data_free);
640 }
641
642 return ad;
643 }
644