1 /* GIMP - The GNU Image Manipulation Program
2  * Copyright (C) 1995 Spencer Kimball and Peter Mattis
3  *
4  * gimptoolpalette.c
5  * Copyright (C) 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 
26 #include "libgimpwidgets/gimpwidgets.h"
27 
28 #include "widgets-types.h"
29 
30 #include "config/gimpguiconfig.h"
31 
32 #include "core/gimp.h"
33 #include "core/gimpcontext.h"
34 #include "core/gimpcontainer.h"
35 #include "core/gimptoolitem.h"
36 
37 #include "gimptoolbox.h"
38 #include "gimptoolbutton.h"
39 #include "gimptoolpalette.h"
40 #include "gimpuimanager.h"
41 #include "gimpwidgets-utils.h"
42 #include "gimpwindowstrategy.h"
43 
44 #include "gimp-intl.h"
45 
46 
47 #define DEFAULT_TOOL_ICON_SIZE GTK_ICON_SIZE_BUTTON
48 #define DEFAULT_BUTTON_RELIEF  GTK_RELIEF_NONE
49 
50 
51 typedef struct _GimpToolPalettePrivate GimpToolPalettePrivate;
52 
53 struct _GimpToolPalettePrivate
54 {
55   GimpToolbox *toolbox;
56 
57   GtkWidget   *group;
58   GHashTable  *buttons;
59 
60   gint         tool_rows;
61   gint         tool_columns;
62 };
63 
64 #define GET_PRIVATE(p) ((GimpToolPalettePrivate *) gimp_tool_palette_get_instance_private ((GimpToolPalette *) (p)))
65 
66 
67 static void       gimp_tool_palette_finalize                  (GObject          *object);
68 
69 static void       gimp_tool_palette_size_allocate             (GtkWidget        *widget,
70                                                                GtkAllocation    *allocation);
71 static void       gimp_tool_palette_style_set                 (GtkWidget        *widget,
72                                                                GtkStyle         *previous_style);
73 
74 static void       gimp_tool_palette_tool_add                  (GimpContainer    *container,
75                                                                GimpToolItem     *tool_item,
76                                                                GimpToolPalette  *palette);
77 static void       gimp_tool_palette_tool_remove               (GimpContainer    *container,
78                                                                GimpToolItem     *tool_item,
79                                                                GimpToolPalette  *palette);
80 static void       gimp_tool_palette_tool_reorder              (GimpContainer    *container,
81                                                                GimpToolItem     *tool_item,
82                                                                gint              index,
83                                                                GimpToolPalette  *palette);
84 
85 static void       gimp_tool_palette_config_menu_mode_notify   (GimpGuiConfig    *config,
86                                                                const GParamSpec *pspec,
87                                                                GimpToolPalette  *palette);
88 static void       gimp_tool_palette_config_size_changed       (GimpGuiConfig    *config,
89                                                                GimpToolPalette  *palette);
90 
91 static void       gimp_tool_palette_add_button                (GimpToolPalette *palette,
92                                                                GimpToolItem    *tool_item,
93                                                                gint             index);
94 
95 static gboolean   gimp_tool_palette_get_show_menu_on_hover    (GimpToolPalette *palette);
96 static void       gimp_tool_palette_update_show_menu_on_hover (GimpToolPalette *palette);
97 
98 
G_DEFINE_TYPE_WITH_PRIVATE(GimpToolPalette,gimp_tool_palette,GTK_TYPE_TOOL_PALETTE)99 G_DEFINE_TYPE_WITH_PRIVATE (GimpToolPalette, gimp_tool_palette,
100                             GTK_TYPE_TOOL_PALETTE)
101 
102 #define parent_class gimp_tool_palette_parent_class
103 
104 
105 static void
106 gimp_tool_palette_class_init (GimpToolPaletteClass *klass)
107 {
108   GObjectClass   *object_class = G_OBJECT_CLASS (klass);
109   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
110 
111   object_class->finalize      = gimp_tool_palette_finalize;
112 
113   widget_class->size_allocate = gimp_tool_palette_size_allocate;
114   widget_class->style_set     = gimp_tool_palette_style_set;
115 
116   gtk_widget_class_install_style_property (widget_class,
117                                            g_param_spec_enum ("tool-icon-size",
118                                                               NULL, NULL,
119                                                               GTK_TYPE_ICON_SIZE,
120                                                               DEFAULT_TOOL_ICON_SIZE,
121                                                               GIMP_PARAM_READABLE));
122 
123   gtk_widget_class_install_style_property (widget_class,
124                                            g_param_spec_enum ("button-relief",
125                                                               NULL, NULL,
126                                                               GTK_TYPE_RELIEF_STYLE,
127                                                               DEFAULT_BUTTON_RELIEF,
128                                                               GIMP_PARAM_READABLE));
129 }
130 
131 static void
gimp_tool_palette_init(GimpToolPalette * palette)132 gimp_tool_palette_init (GimpToolPalette *palette)
133 {
134   GimpToolPalettePrivate *private = GET_PRIVATE (palette);
135 
136   private->buttons = g_hash_table_new (g_direct_hash, g_direct_equal);
137 
138   gtk_tool_palette_set_style (GTK_TOOL_PALETTE (palette), GTK_TOOLBAR_ICONS);
139 }
140 
141 static void
gimp_tool_palette_finalize(GObject * object)142 gimp_tool_palette_finalize (GObject *object)
143 {
144   GimpToolPalettePrivate *private = GET_PRIVATE (object);
145 
146   g_clear_pointer (&private->buttons, g_hash_table_unref);
147 
148   if (private->toolbox)
149     {
150       GimpContext *context = gimp_toolbox_get_context (private->toolbox);
151 
152       if (context)
153         {
154           g_signal_handlers_disconnect_by_func (
155             context->gimp->config,
156             G_CALLBACK (gimp_tool_palette_config_menu_mode_notify),
157             object);
158           g_signal_handlers_disconnect_by_func (
159             context->gimp->config,
160             G_CALLBACK (gimp_tool_palette_config_size_changed),
161             object);
162         }
163     }
164 
165   G_OBJECT_CLASS (parent_class)->finalize (object);
166 }
167 
168 static void
gimp_tool_palette_size_allocate(GtkWidget * widget,GtkAllocation * allocation)169 gimp_tool_palette_size_allocate (GtkWidget     *widget,
170                                  GtkAllocation *allocation)
171 {
172   GimpToolPalette        *palette = GIMP_TOOL_PALETTE (widget);
173   GimpToolPalettePrivate *private = GET_PRIVATE (widget);
174   gint                    button_width;
175   gint                    button_height;
176 
177   GTK_WIDGET_CLASS (parent_class)->size_allocate (widget, allocation);
178 
179   if (gimp_tool_palette_get_button_size (palette,
180                                          &button_width, &button_height))
181     {
182       GimpToolItem   *tool_item;
183       GHashTableIter  iter;
184       gint            n_tools;
185       gint            tool_rows;
186       gint            tool_columns;
187 
188       n_tools = 0;
189 
190       g_hash_table_iter_init (&iter, private->buttons);
191 
192       while (g_hash_table_iter_next (&iter, (gpointer *) &tool_item, NULL))
193         {
194           if (gimp_tool_item_get_visible (tool_item))
195             n_tools++;
196         }
197 
198       tool_columns = MAX (1, (allocation->width / button_width));
199       tool_rows    = n_tools / tool_columns;
200 
201       if (n_tools % tool_columns)
202         tool_rows++;
203 
204       if (private->tool_rows    != tool_rows  ||
205           private->tool_columns != tool_columns)
206         {
207           private->tool_rows    = tool_rows;
208           private->tool_columns = tool_columns;
209 
210           gtk_widget_set_size_request (widget, -1,
211                                        tool_rows * button_height);
212 
213           gimp_tool_palette_update_show_menu_on_hover (palette);
214         }
215     }
216 }
217 
218 static void
gimp_tool_palette_style_set(GtkWidget * widget,GtkStyle * previous_style)219 gimp_tool_palette_style_set (GtkWidget *widget,
220                              GtkStyle  *previous_style)
221 {
222   GimpToolPalettePrivate *private = GET_PRIVATE (widget);
223   Gimp                   *gimp;
224   GtkWidget              *tool_button;
225   GHashTableIter          iter;
226   GtkReliefStyle          relief;
227 
228   GTK_WIDGET_CLASS (parent_class)->style_set (widget, previous_style);
229 
230   if (! gimp_toolbox_get_context (private->toolbox))
231     return;
232 
233   gimp = gimp_toolbox_get_context (private->toolbox)->gimp;
234 
235   gtk_widget_style_get (widget,
236                         "button-relief", &relief,
237                         NULL);
238 
239   gimp_tool_palette_config_size_changed (GIMP_GUI_CONFIG (gimp->config),
240                                          GIMP_TOOL_PALETTE (widget));
241 
242   g_hash_table_iter_init (&iter, private->buttons);
243 
244   while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &tool_button))
245     {
246       GtkWidget *button = gtk_bin_get_child (GTK_BIN (tool_button));
247 
248       gtk_button_set_relief (GTK_BUTTON (button), relief);
249     }
250 
251   gimp_dock_invalidate_geometry (GIMP_DOCK (private->toolbox));
252 }
253 
254 
255 /*  public functions  */
256 
257 GtkWidget *
gimp_tool_palette_new(void)258 gimp_tool_palette_new (void)
259 {
260   return g_object_new (GIMP_TYPE_TOOL_PALETTE, NULL);
261 }
262 
263 void
gimp_tool_palette_set_toolbox(GimpToolPalette * palette,GimpToolbox * toolbox)264 gimp_tool_palette_set_toolbox (GimpToolPalette *palette,
265                                GimpToolbox     *toolbox)
266 {
267   GimpToolPalettePrivate *private;
268   GimpContext            *context;
269   GList                  *list;
270 
271   g_return_if_fail (GIMP_IS_TOOL_PALETTE (palette));
272   g_return_if_fail (GIMP_IS_TOOLBOX (toolbox));
273 
274   private = GET_PRIVATE (palette);
275 
276   if (private->toolbox)
277     {
278       context = gimp_toolbox_get_context (private->toolbox);
279 
280       g_signal_handlers_disconnect_by_func (
281         GIMP_GUI_CONFIG (context->gimp->config),
282         G_CALLBACK (gimp_tool_palette_config_menu_mode_notify),
283         palette);
284       g_signal_handlers_disconnect_by_func (
285         GIMP_GUI_CONFIG (context->gimp->config),
286         G_CALLBACK (gimp_tool_palette_config_size_changed),
287         palette);
288     }
289 
290   private->toolbox = toolbox;
291 
292   context = gimp_toolbox_get_context (toolbox);
293 
294   private->group = gtk_tool_item_group_new (_("Tools"));
295   gtk_tool_item_group_set_label_widget (GTK_TOOL_ITEM_GROUP (private->group),
296                                         NULL);
297   gtk_container_add (GTK_CONTAINER (palette), private->group);
298   gtk_widget_show (private->group);
299 
300   for (list = gimp_get_tool_item_ui_iter (context->gimp);
301        list;
302        list = g_list_next (list))
303     {
304       GimpToolItem *tool_item = list->data;
305 
306       gimp_tool_palette_add_button (palette, tool_item, -1);
307     }
308 
309   g_signal_connect_object (context->gimp->tool_item_ui_list, "add",
310                            G_CALLBACK (gimp_tool_palette_tool_add),
311                            palette, 0);
312   g_signal_connect_object (context->gimp->tool_item_ui_list, "remove",
313                            G_CALLBACK (gimp_tool_palette_tool_remove),
314                            palette, 0);
315   g_signal_connect_object (context->gimp->tool_item_ui_list, "reorder",
316                            G_CALLBACK (gimp_tool_palette_tool_reorder),
317                            palette, 0);
318 
319   g_signal_connect (GIMP_GUI_CONFIG (context->gimp->config),
320                     "notify::toolbox-group-menu-mode",
321                     G_CALLBACK (gimp_tool_palette_config_menu_mode_notify),
322                     palette);
323   gimp_tool_palette_update_show_menu_on_hover (palette);
324 
325   /* Update the toolbox icon size on config change. */
326   g_signal_connect (GIMP_GUI_CONFIG (context->gimp->config),
327                     "size-changed",
328                     G_CALLBACK (gimp_tool_palette_config_size_changed),
329                     palette);
330   gimp_tool_palette_config_size_changed (GIMP_GUI_CONFIG (context->gimp->config),
331                                          palette);
332 }
333 
334 gboolean
gimp_tool_palette_get_button_size(GimpToolPalette * palette,gint * width,gint * height)335 gimp_tool_palette_get_button_size (GimpToolPalette *palette,
336                                    gint            *width,
337                                    gint            *height)
338 {
339   GimpToolPalettePrivate *private;
340   GHashTableIter          iter;
341   GtkWidget              *tool_button;
342 
343   g_return_val_if_fail (GIMP_IS_TOOL_PALETTE (palette), FALSE);
344   g_return_val_if_fail (width != NULL, FALSE);
345   g_return_val_if_fail (height != NULL, FALSE);
346 
347   private = GET_PRIVATE (palette);
348 
349   g_hash_table_iter_init (&iter, private->buttons);
350 
351   if (g_hash_table_iter_next (&iter, NULL, (gpointer *) &tool_button))
352     {
353       GtkRequisition button_requisition;
354 
355       gtk_widget_size_request (tool_button, &button_requisition);
356 
357       *width  = button_requisition.width;
358       *height = button_requisition.height;
359 
360       return TRUE;
361     }
362 
363   return FALSE;
364 }
365 
366 
367 /*  private functions  */
368 
369 static void
gimp_tool_palette_tool_add(GimpContainer * container,GimpToolItem * tool_item,GimpToolPalette * palette)370 gimp_tool_palette_tool_add (GimpContainer   *container,
371                             GimpToolItem    *tool_item,
372                             GimpToolPalette *palette)
373 {
374   gimp_tool_palette_add_button (
375     palette,
376     tool_item,
377     gimp_container_get_child_index (container, GIMP_OBJECT (tool_item)));
378 }
379 
380 static void
gimp_tool_palette_tool_remove(GimpContainer * container,GimpToolItem * tool_item,GimpToolPalette * palette)381 gimp_tool_palette_tool_remove (GimpContainer   *container,
382                                GimpToolItem    *tool_item,
383                                GimpToolPalette *palette)
384 {
385   GimpToolPalettePrivate *private = GET_PRIVATE (palette);
386   GtkWidget              *tool_button;
387 
388   tool_button = g_hash_table_lookup (private->buttons, tool_item);
389 
390   if (tool_button)
391     {
392       g_hash_table_remove (private->buttons, tool_item);
393 
394       gtk_container_remove (GTK_CONTAINER (private->group), tool_button);
395     }
396 }
397 
398 static void
gimp_tool_palette_tool_reorder(GimpContainer * container,GimpToolItem * tool_item,gint index,GimpToolPalette * palette)399 gimp_tool_palette_tool_reorder (GimpContainer   *container,
400                                 GimpToolItem    *tool_item,
401                                 gint             index,
402                                 GimpToolPalette *palette)
403 {
404   GimpToolPalettePrivate *private = GET_PRIVATE (palette);
405   GtkWidget              *tool_button;
406 
407   tool_button = g_hash_table_lookup (private->buttons, tool_item);
408 
409   if (tool_button)
410     {
411       gtk_tool_item_group_set_item_position (
412         GTK_TOOL_ITEM_GROUP (private->group),
413         GTK_TOOL_ITEM (tool_button), index);
414     }
415 }
416 
417 static void
gimp_tool_palette_config_menu_mode_notify(GimpGuiConfig * config,const GParamSpec * pspec,GimpToolPalette * palette)418 gimp_tool_palette_config_menu_mode_notify (GimpGuiConfig    *config,
419                                            const GParamSpec *pspec,
420                                            GimpToolPalette  *palette)
421 {
422   gimp_tool_palette_update_show_menu_on_hover (palette);
423 }
424 
425 static void
gimp_tool_palette_config_size_changed(GimpGuiConfig * config,GimpToolPalette * palette)426 gimp_tool_palette_config_size_changed (GimpGuiConfig   *config,
427                                        GimpToolPalette *palette)
428 {
429   GimpIconSize size;
430   GtkIconSize  tool_icon_size;
431 
432   size = gimp_gui_config_detect_icon_size (config);
433   /* Match GimpIconSize with GtkIconSize for the toolbox icons. */
434   switch (size)
435     {
436     case GIMP_ICON_SIZE_SMALL:
437       tool_icon_size = GTK_ICON_SIZE_SMALL_TOOLBAR;
438       break;
439     case GIMP_ICON_SIZE_MEDIUM:
440       tool_icon_size = GTK_ICON_SIZE_LARGE_TOOLBAR;
441       break;
442     case GIMP_ICON_SIZE_LARGE:
443       tool_icon_size = GTK_ICON_SIZE_DND;
444       break;
445     case GIMP_ICON_SIZE_HUGE:
446       tool_icon_size = GTK_ICON_SIZE_DIALOG;
447       break;
448     default:
449       /* GIMP_ICON_SIZE_DEFAULT:
450        * let's use the size set by the theme. */
451       gtk_widget_style_get (GTK_WIDGET (palette),
452                             "tool-icon-size", &tool_icon_size,
453                             NULL);
454       break;
455     }
456 
457   gtk_tool_palette_set_icon_size (GTK_TOOL_PALETTE (palette), tool_icon_size);
458 }
459 
460 static void
gimp_tool_palette_add_button(GimpToolPalette * palette,GimpToolItem * tool_item,gint index)461 gimp_tool_palette_add_button (GimpToolPalette *palette,
462                               GimpToolItem    *tool_item,
463                               gint             index)
464 {
465   GimpToolPalettePrivate *private = GET_PRIVATE (palette);
466   GtkToolItem            *tool_button;
467   GtkWidget              *button;
468   GtkReliefStyle          relief;
469 
470   tool_button = gimp_tool_button_new (private->toolbox, tool_item);
471   gtk_tool_item_group_insert (GTK_TOOL_ITEM_GROUP (private->group),
472                               tool_button, index);
473   gimp_tool_button_set_show_menu_on_hover (
474     GIMP_TOOL_BUTTON (tool_button),
475     gimp_tool_palette_get_show_menu_on_hover (palette));
476   gtk_widget_show (GTK_WIDGET (tool_button));
477 
478   g_object_bind_property (tool_item,   "shown",
479                           tool_button, "visible-horizontal",
480                           G_BINDING_SYNC_CREATE);
481   g_object_bind_property (tool_item,   "shown",
482                           tool_button, "visible-vertical",
483                           G_BINDING_SYNC_CREATE);
484 
485   button = gtk_bin_get_child (GTK_BIN (tool_button));
486 
487   gtk_widget_style_get (GTK_WIDGET (palette),
488                         "button-relief", &relief,
489                         NULL);
490 
491   gtk_button_set_relief (GTK_BUTTON (button), relief);
492 
493   g_hash_table_insert (private->buttons, tool_item, tool_button);
494 }
495 
496 static gboolean
gimp_tool_palette_get_show_menu_on_hover(GimpToolPalette * palette)497 gimp_tool_palette_get_show_menu_on_hover (GimpToolPalette *palette)
498 {
499   GimpToolPalettePrivate *private = GET_PRIVATE (palette);
500 
501   if (private->toolbox)
502     {
503       GimpContext *context = gimp_toolbox_get_context (private->toolbox);
504 
505       if (context)
506         {
507           GimpGuiConfig *config = GIMP_GUI_CONFIG (context->gimp->config);
508 
509           switch (config->toolbox_group_menu_mode)
510             {
511             case GIMP_TOOL_GROUP_MENU_MODE_SHOW_ON_CLICK:
512               return FALSE;
513 
514             case GIMP_TOOL_GROUP_MENU_MODE_SHOW_ON_HOVER:
515               return TRUE;
516 
517             case GIMP_TOOL_GROUP_MENU_MODE_SHOW_ON_HOVER_SINGLE_COLUMN:
518               return private->tool_columns == 1;
519             }
520         }
521     }
522 
523   return FALSE;
524 }
525 
526 static void
gimp_tool_palette_update_show_menu_on_hover(GimpToolPalette * palette)527 gimp_tool_palette_update_show_menu_on_hover (GimpToolPalette *palette)
528 {
529   GimpToolPalettePrivate *private = GET_PRIVATE (palette);
530   GHashTableIter          iter;
531   GimpToolButton         *tool_button;
532   gboolean                show_menu_on_hover;
533 
534   show_menu_on_hover = gimp_tool_palette_get_show_menu_on_hover (palette);
535 
536   g_hash_table_iter_init (&iter, private->buttons);
537 
538   while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &tool_button))
539     {
540       gimp_tool_button_set_show_menu_on_hover (tool_button, show_menu_on_hover);
541     }
542 }
543