1 /* GIMP - The GNU Image Manipulation Program
2  * Copyright (C) 1995 Spencer Kimball and Peter Mattis
3  *
4  * gimptoolgroup.c
5  * Copyright (C) 2020 Ell
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 <string.h>
24 
25 #include <gdk-pixbuf/gdk-pixbuf.h>
26 #include <gegl.h>
27 
28 #include "libgimpbase/gimpbase.h"
29 #include "libgimpconfig/gimpconfig.h"
30 
31 #include "core-types.h"
32 
33 #include "gimplist.h"
34 #include "gimpmarshal.h"
35 #include "gimptoolgroup.h"
36 #include "gimptoolinfo.h"
37 
38 #include "gimp-intl.h"
39 
40 
41 enum
42 {
43   ACTIVE_TOOL_CHANGED,
44   LAST_SIGNAL
45 };
46 
47 enum
48 {
49   PROP_0,
50   PROP_ACTIVE_TOOL,
51   PROP_CHILDREN
52 };
53 
54 
55 struct _GimpToolGroupPrivate
56 {
57   gchar         *active_tool;
58   GimpContainer *children;
59 };
60 
61 
62 /*  local function prototypes  */
63 
64 
65 static void            gimp_tool_group_finalize        (GObject        *object);
66 static void            gimp_tool_group_get_property    (GObject        *object,
67                                                         guint           property_id,
68                                                         GValue         *value,
69                                                         GParamSpec     *pspec);
70 static void            gimp_tool_group_set_property    (GObject        *object,
71                                                         guint           property_id,
72                                                         const GValue   *value,
73                                                         GParamSpec     *pspec);
74 
75 static gint64          gimp_tool_group_get_memsize     (GimpObject     *object,
76                                                         gint64         *gui_size);
77 
78 static gchar         * gimp_tool_group_get_description (GimpViewable   *viewable,
79                                                         gchar         **tooltip);
80 static GimpContainer * gimp_tool_group_get_children    (GimpViewable   *viewable);
81 static void            gimp_tool_group_set_expanded    (GimpViewable   *viewable,
82                                                         gboolean        expand);
83 static gboolean        gimp_tool_group_get_expanded    (GimpViewable   *viewable);
84 
85 static void            gimp_tool_group_child_add       (GimpContainer  *container,
86                                                         GimpToolInfo   *tool_info,
87                                                         GimpToolGroup  *tool_group);
88 static void            gimp_tool_group_child_remove    (GimpContainer  *container,
89                                                         GimpToolInfo   *tool_info,
90                                                         GimpToolGroup  *tool_group);
91 
92 static void            gimp_tool_group_shown_changed   (GimpToolItem   *tool_item);
93 
94 
95 G_DEFINE_TYPE_WITH_PRIVATE (GimpToolGroup, gimp_tool_group, GIMP_TYPE_TOOL_ITEM)
96 
97 #define parent_class gimp_tool_group_parent_class
98 
99 static guint gimp_tool_group_signals[LAST_SIGNAL] = { 0 };
100 
101 
102 /*  private functions  */
103 
104 static void
gimp_tool_group_class_init(GimpToolGroupClass * klass)105 gimp_tool_group_class_init (GimpToolGroupClass *klass)
106 {
107   GObjectClass      *object_class      = G_OBJECT_CLASS (klass);
108   GimpObjectClass   *gimp_object_class = GIMP_OBJECT_CLASS (klass);
109   GimpViewableClass *viewable_class    = GIMP_VIEWABLE_CLASS (klass);
110   GimpToolItemClass *tool_item_class   = GIMP_TOOL_ITEM_CLASS (klass);
111 
112   gimp_tool_group_signals[ACTIVE_TOOL_CHANGED] =
113     g_signal_new ("active-tool-changed",
114                   G_TYPE_FROM_CLASS (klass),
115                   G_SIGNAL_RUN_FIRST,
116                   G_STRUCT_OFFSET (GimpToolGroupClass, active_tool_changed),
117                   NULL, NULL,
118                   gimp_marshal_VOID__VOID,
119                   G_TYPE_NONE, 0);
120 
121   object_class->finalize            = gimp_tool_group_finalize;
122   object_class->get_property        = gimp_tool_group_get_property;
123   object_class->set_property        = gimp_tool_group_set_property;
124 
125   gimp_object_class->get_memsize    = gimp_tool_group_get_memsize;
126 
127   viewable_class->default_icon_name = "folder";
128   viewable_class->get_description   = gimp_tool_group_get_description;
129   viewable_class->get_children      = gimp_tool_group_get_children;
130   viewable_class->get_expanded      = gimp_tool_group_get_expanded;
131   viewable_class->set_expanded      = gimp_tool_group_set_expanded;
132 
133   tool_item_class->shown_changed    = gimp_tool_group_shown_changed;
134 
135   GIMP_CONFIG_PROP_STRING (object_class, PROP_ACTIVE_TOOL,
136                            "active-tool", NULL, NULL,
137                            NULL,
138                            GIMP_PARAM_STATIC_STRINGS);
139 
140   GIMP_CONFIG_PROP_OBJECT (object_class, PROP_CHILDREN,
141                            "children", NULL, NULL,
142                            GIMP_TYPE_CONTAINER,
143                            GIMP_PARAM_STATIC_STRINGS |
144                            GIMP_CONFIG_PARAM_AGGREGATE);
145 }
146 
147 static void
gimp_tool_group_init(GimpToolGroup * tool_group)148 gimp_tool_group_init (GimpToolGroup *tool_group)
149 {
150   tool_group->priv = gimp_tool_group_get_instance_private (tool_group);
151 
152   tool_group->priv->children = g_object_new (
153     GIMP_TYPE_LIST,
154     "children-type", GIMP_TYPE_TOOL_INFO,
155     "append",        TRUE,
156     NULL);
157 
158   g_signal_connect (tool_group->priv->children, "add",
159                     G_CALLBACK (gimp_tool_group_child_add),
160                     tool_group);
161   g_signal_connect (tool_group->priv->children, "remove",
162                     G_CALLBACK (gimp_tool_group_child_remove),
163                     tool_group);
164 }
165 
166 static void
gimp_tool_group_finalize(GObject * object)167 gimp_tool_group_finalize (GObject *object)
168 {
169   GimpToolGroup *tool_group = GIMP_TOOL_GROUP (object);
170 
171   g_clear_pointer (&tool_group->priv->active_tool, g_free);
172 
173   g_clear_object (&tool_group->priv->children);
174 
175   G_OBJECT_CLASS (parent_class)->finalize (object);
176 }
177 
178 static void
gimp_tool_group_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)179 gimp_tool_group_get_property (GObject    *object,
180                               guint       property_id,
181                               GValue     *value,
182                               GParamSpec *pspec)
183 {
184   GimpToolGroup *tool_group = GIMP_TOOL_GROUP (object);
185 
186   switch (property_id)
187     {
188     case PROP_ACTIVE_TOOL:
189       g_value_set_string (value, tool_group->priv->active_tool);
190       break;
191 
192     case PROP_CHILDREN:
193       g_value_set_object (value, tool_group->priv->children);
194       break;
195 
196     default:
197       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
198       break;
199     }
200 }
201 
202 static void
gimp_tool_group_set_property_add_tool(GimpToolInfo * tool_info,GimpToolGroup * tool_group)203 gimp_tool_group_set_property_add_tool (GimpToolInfo  *tool_info,
204                                        GimpToolGroup *tool_group)
205 {
206   gimp_container_add (tool_group->priv->children, GIMP_OBJECT (tool_info));
207 }
208 
209 static void
gimp_tool_group_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)210 gimp_tool_group_set_property (GObject      *object,
211                               guint         property_id,
212                               const GValue *value,
213                               GParamSpec   *pspec)
214 {
215   GimpToolGroup *tool_group = GIMP_TOOL_GROUP (object);
216 
217   switch (property_id)
218     {
219     case PROP_ACTIVE_TOOL:
220       g_free (tool_group->priv->active_tool);
221 
222       tool_group->priv->active_tool = g_value_dup_string (value);
223       break;
224 
225     case PROP_CHILDREN:
226       {
227         GimpContainer *container = g_value_get_object (value);
228 
229         gimp_container_clear (tool_group->priv->children);
230 
231         if (! container)
232           break;
233 
234         gimp_container_foreach (container,
235                                 (GFunc) gimp_tool_group_set_property_add_tool,
236                                 tool_group);
237       }
238       break;
239 
240     default:
241       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
242       break;
243     }
244 }
245 
246 static gint64
gimp_tool_group_get_memsize(GimpObject * object,gint64 * gui_size)247 gimp_tool_group_get_memsize (GimpObject *object,
248                              gint64     *gui_size)
249 {
250   GimpToolGroup *tool_group = GIMP_TOOL_GROUP (object);
251   gint64         memsize = 0;
252 
253   memsize += gimp_object_get_memsize (GIMP_OBJECT (tool_group->priv->children),
254                                       gui_size);
255 
256   return memsize + GIMP_OBJECT_CLASS (parent_class)->get_memsize (object,
257                                                                   gui_size);
258 }
259 
260 static gchar *
gimp_tool_group_get_description(GimpViewable * viewable,gchar ** tooltip)261 gimp_tool_group_get_description (GimpViewable  *viewable,
262                                  gchar        **tooltip)
263 {
264   /* Translators: this is a noun */
265   return g_strdup (C_("tool-item", "Group"));
266 }
267 
268 static GimpContainer *
gimp_tool_group_get_children(GimpViewable * viewable)269 gimp_tool_group_get_children (GimpViewable *viewable)
270 {
271   GimpToolGroup *tool_group = GIMP_TOOL_GROUP (viewable);
272 
273   return tool_group->priv->children;
274 }
275 
276 static void
gimp_tool_group_set_expanded(GimpViewable * viewable,gboolean expand)277 gimp_tool_group_set_expanded (GimpViewable *viewable,
278                               gboolean      expand)
279 {
280   if (! expand)
281     gimp_viewable_expanded_changed (viewable);
282 }
283 
284 static gboolean
gimp_tool_group_get_expanded(GimpViewable * viewable)285 gimp_tool_group_get_expanded (GimpViewable *viewable)
286 {
287   return TRUE;
288 }
289 
290 static void
gimp_tool_group_child_add(GimpContainer * container,GimpToolInfo * tool_info,GimpToolGroup * tool_group)291 gimp_tool_group_child_add (GimpContainer *container,
292                            GimpToolInfo  *tool_info,
293                            GimpToolGroup *tool_group)
294 {
295   g_return_if_fail (
296     gimp_viewable_get_parent (GIMP_VIEWABLE (tool_info)) == NULL);
297 
298   gimp_viewable_set_parent (GIMP_VIEWABLE (tool_info),
299                             GIMP_VIEWABLE (tool_group));
300 
301   if (! tool_group->priv->active_tool)
302     gimp_tool_group_set_active_tool_info (tool_group, tool_info);
303 }
304 
305 static void
gimp_tool_group_child_remove(GimpContainer * container,GimpToolInfo * tool_info,GimpToolGroup * tool_group)306 gimp_tool_group_child_remove (GimpContainer *container,
307                               GimpToolInfo  *tool_info,
308                               GimpToolGroup *tool_group)
309 {
310   gimp_viewable_set_parent (GIMP_VIEWABLE (tool_info), NULL);
311 
312   if (! g_strcmp0 (tool_group->priv->active_tool,
313                    gimp_object_get_name (GIMP_OBJECT (tool_info))))
314     {
315       GimpToolInfo *active_tool_info = NULL;
316 
317       if (! gimp_container_is_empty (tool_group->priv->children))
318         {
319           active_tool_info = GIMP_TOOL_INFO (
320             gimp_container_get_first_child (tool_group->priv->children));
321         }
322 
323       gimp_tool_group_set_active_tool_info (tool_group, active_tool_info);
324     }
325 }
326 
327 static void
gimp_tool_group_shown_changed(GimpToolItem * tool_item)328 gimp_tool_group_shown_changed (GimpToolItem *tool_item)
329 {
330   GimpToolGroup *tool_group = GIMP_TOOL_GROUP (tool_item);
331   GList         *iter;
332 
333   if (GIMP_TOOL_ITEM_CLASS (parent_class)->shown_changed)
334     GIMP_TOOL_ITEM_CLASS (parent_class)->shown_changed (tool_item);
335 
336   for (iter = GIMP_LIST (tool_group->priv->children)->queue->head;
337        iter;
338        iter = g_list_next (iter))
339     {
340       GimpToolItem *tool_item = iter->data;
341 
342       if (gimp_tool_item_get_visible (tool_item))
343         gimp_tool_item_shown_changed (tool_item);
344     }
345 }
346 
347 
348 /*  public functions  */
349 
350 GimpToolGroup *
gimp_tool_group_new(void)351 gimp_tool_group_new (void)
352 {
353   GimpToolGroup *tool_group;
354 
355   tool_group = g_object_new (GIMP_TYPE_TOOL_GROUP, NULL);
356 
357   gimp_object_set_static_name (GIMP_OBJECT (tool_group), "tool group");
358 
359   return tool_group;
360 }
361 
362 void
gimp_tool_group_set_active_tool(GimpToolGroup * tool_group,const gchar * tool_name)363 gimp_tool_group_set_active_tool (GimpToolGroup *tool_group,
364                                  const gchar   *tool_name)
365 {
366   g_return_if_fail (GIMP_IS_TOOL_GROUP (tool_group));
367 
368   if (g_strcmp0 (tool_group->priv->active_tool, tool_name))
369     {
370       g_return_if_fail (tool_name == NULL ||
371                         gimp_container_get_child_by_name (
372                           tool_group->priv->children, tool_name) != NULL);
373 
374       g_free (tool_group->priv->active_tool);
375 
376       tool_group->priv->active_tool = g_strdup (tool_name);;
377 
378       g_signal_emit (tool_group,
379                      gimp_tool_group_signals[ACTIVE_TOOL_CHANGED], 0);
380 
381       g_object_notify (G_OBJECT (tool_group), "active-tool");
382     }
383 }
384 
385 const gchar *
gimp_tool_group_get_active_tool(GimpToolGroup * tool_group)386 gimp_tool_group_get_active_tool (GimpToolGroup *tool_group)
387 {
388   g_return_val_if_fail (GIMP_IS_TOOL_GROUP (tool_group), NULL);
389 
390   return tool_group->priv->active_tool;
391 }
392 
393 void
gimp_tool_group_set_active_tool_info(GimpToolGroup * tool_group,GimpToolInfo * tool_info)394 gimp_tool_group_set_active_tool_info (GimpToolGroup *tool_group,
395                                       GimpToolInfo  *tool_info)
396 {
397   g_return_if_fail (GIMP_IS_TOOL_GROUP (tool_group));
398   g_return_if_fail (tool_info == NULL || GIMP_IS_TOOL_INFO (tool_info));
399 
400   gimp_tool_group_set_active_tool (
401     tool_group,
402     tool_info ? gimp_object_get_name (GIMP_OBJECT (tool_info)) : NULL);
403 }
404 
405 GimpToolInfo *
gimp_tool_group_get_active_tool_info(GimpToolGroup * tool_group)406 gimp_tool_group_get_active_tool_info (GimpToolGroup *tool_group)
407 {
408   g_return_val_if_fail (GIMP_IS_TOOL_GROUP (tool_group), NULL);
409 
410   return GIMP_TOOL_INFO (
411     gimp_container_get_child_by_name (tool_group->priv->children,
412                                       tool_group->priv->active_tool));
413 }
414