1 /* GIMP - The GNU Image Manipulation Program
2  * Copyright (C) 1995 Spencer Kimball and Peter Mattis
3  *
4  * This program is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 3 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
16  */
17 
18 #include "config.h"
19 
20 #include <gdk-pixbuf/gdk-pixbuf.h>
21 #include <gegl.h>
22 
23 #include "libgimpbase/gimpbase.h"
24 
25 #include "core-types.h"
26 
27 #include "gimpcontext.h"
28 #include "gimpimage.h"
29 #include "gimpimage-item-list.h"
30 #include "gimpimage-undo.h"
31 #include "gimpitem.h"
32 #include "gimpobjectqueue.h"
33 #include "gimpprogress.h"
34 
35 #include "gimp-intl.h"
36 
37 
38 /*  public functions  */
39 
40 gboolean
gimp_image_item_list_bounds(GimpImage * image,GList * list,gint * x,gint * y,gint * width,gint * height)41 gimp_image_item_list_bounds (GimpImage *image,
42                              GList     *list,
43                              gint      *x,
44                              gint      *y,
45                              gint      *width,
46                              gint      *height)
47 {
48   GList    *l;
49   gboolean  bounds = FALSE;
50 
51   g_return_val_if_fail (GIMP_IS_IMAGE (image), FALSE);
52   g_return_val_if_fail (x != 0, FALSE);
53   g_return_val_if_fail (y != 0, FALSE);
54   g_return_val_if_fail (width != 0, FALSE);
55   g_return_val_if_fail (height != 0, FALSE);
56 
57   for (l = list; l; l = g_list_next (l))
58     {
59       GimpItem *item = l->data;
60       gint      tmp_x, tmp_y;
61       gint      tmp_w, tmp_h;
62 
63       if (gimp_item_bounds (item, &tmp_x, &tmp_y, &tmp_w, &tmp_h))
64         {
65           gint off_x, off_y;
66 
67           gimp_item_get_offset (item, &off_x, &off_y);
68 
69           if (bounds)
70             {
71               gimp_rectangle_union (*x, *y, *width, *height,
72                                     tmp_x + off_x, tmp_y + off_y,
73                                     tmp_w, tmp_h,
74                                     x, y, width, height);
75             }
76           else
77             {
78               *x      = tmp_x + off_x;
79               *y      = tmp_y + off_y;
80               *width  = tmp_w;
81               *height = tmp_h;
82 
83               bounds = TRUE;
84             }
85         }
86     }
87 
88   if (! bounds)
89     {
90       *x      = 0;
91       *y      = 0;
92       *width  = gimp_image_get_width  (image);
93       *height = gimp_image_get_height (image);
94     }
95 
96   return bounds;
97 }
98 
99 void
gimp_image_item_list_translate(GimpImage * image,GList * list,gint offset_x,gint offset_y,gboolean push_undo)100 gimp_image_item_list_translate (GimpImage *image,
101                                 GList     *list,
102                                 gint       offset_x,
103                                 gint       offset_y,
104                                 gboolean   push_undo)
105 {
106   g_return_if_fail (GIMP_IS_IMAGE (image));
107 
108   if (list)
109     {
110       GList *l;
111 
112       if (list->next)
113         {
114           if (push_undo)
115             {
116               gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_ITEM_DISPLACE,
117                                            C_("undo-type", "Translate Items"));
118             }
119 
120           for (l = list; l; l = g_list_next (l))
121             gimp_item_start_transform (GIMP_ITEM (l->data), push_undo);
122         }
123 
124       for (l = list; l; l = g_list_next (l))
125         gimp_item_translate (GIMP_ITEM (l->data),
126                              offset_x, offset_y, push_undo);
127 
128       if (list->next)
129         {
130           for (l = list; l; l = g_list_next (l))
131             gimp_item_end_transform (GIMP_ITEM (l->data), push_undo);
132 
133           if (push_undo)
134             gimp_image_undo_group_end (image);
135         }
136     }
137 }
138 
139 void
gimp_image_item_list_flip(GimpImage * image,GList * list,GimpContext * context,GimpOrientationType flip_type,gdouble axis,gboolean clip_result)140 gimp_image_item_list_flip (GimpImage           *image,
141                            GList               *list,
142                            GimpContext         *context,
143                            GimpOrientationType  flip_type,
144                            gdouble              axis,
145                            gboolean             clip_result)
146 {
147   g_return_if_fail (GIMP_IS_IMAGE (image));
148   g_return_if_fail (GIMP_IS_CONTEXT (context));
149 
150   if (list)
151     {
152       GList *l;
153 
154       if (list->next)
155         {
156           gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_TRANSFORM,
157                                        C_("undo-type", "Flip Items"));
158 
159           for (l = list; l; l = g_list_next (l))
160             gimp_item_start_transform (GIMP_ITEM (l->data), TRUE);
161         }
162 
163       for (l = list; l; l = g_list_next (l))
164         {
165           GimpItem *item = l->data;
166 
167           gimp_item_flip (item, context,
168                           flip_type, axis,
169                           gimp_item_get_clip (item, clip_result));
170         }
171 
172       if (list->next)
173         {
174           for (l = list; l; l = g_list_next (l))
175             gimp_item_end_transform (GIMP_ITEM (l->data), TRUE);
176 
177           gimp_image_undo_group_end (image);
178         }
179     }
180 }
181 
182 void
gimp_image_item_list_rotate(GimpImage * image,GList * list,GimpContext * context,GimpRotationType rotate_type,gdouble center_x,gdouble center_y,gboolean clip_result)183 gimp_image_item_list_rotate (GimpImage        *image,
184                              GList            *list,
185                              GimpContext      *context,
186                              GimpRotationType  rotate_type,
187                              gdouble           center_x,
188                              gdouble           center_y,
189                              gboolean          clip_result)
190 {
191   g_return_if_fail (GIMP_IS_IMAGE (image));
192   g_return_if_fail (GIMP_IS_CONTEXT (context));
193 
194   if (list)
195     {
196       GList *l;
197 
198       if (list->next)
199         {
200           gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_TRANSFORM,
201                                        C_("undo-type", "Rotate Items"));
202 
203           for (l = list; l; l = g_list_next (l))
204             gimp_item_start_transform (GIMP_ITEM (l->data), TRUE);
205         }
206 
207       for (l = list; l; l = g_list_next (l))
208         {
209           GimpItem *item = l->data;
210 
211           gimp_item_rotate (item, context,
212                             rotate_type, center_x, center_y,
213                             gimp_item_get_clip (item, clip_result));
214         }
215 
216       if (list->next)
217         {
218           for (l = list; l; l = g_list_next (l))
219             gimp_item_end_transform (GIMP_ITEM (l->data), TRUE);
220 
221           gimp_image_undo_group_end (image);
222         }
223     }
224 }
225 
226 void
gimp_image_item_list_transform(GimpImage * image,GList * list,GimpContext * context,const GimpMatrix3 * matrix,GimpTransformDirection direction,GimpInterpolationType interpolation_type,GimpTransformResize clip_result,GimpProgress * progress)227 gimp_image_item_list_transform (GimpImage              *image,
228                                 GList                  *list,
229                                 GimpContext            *context,
230                                 const GimpMatrix3      *matrix,
231                                 GimpTransformDirection  direction,
232                                 GimpInterpolationType   interpolation_type,
233                                 GimpTransformResize     clip_result,
234                                 GimpProgress           *progress)
235 {
236   g_return_if_fail (GIMP_IS_IMAGE (image));
237   g_return_if_fail (GIMP_IS_CONTEXT (context));
238   g_return_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress));
239 
240   if (list)
241     {
242       GimpObjectQueue *queue = NULL;
243       GList           *l;
244 
245       if (progress)
246         {
247           queue    = gimp_object_queue_new (progress);
248           progress = GIMP_PROGRESS (queue);
249 
250           gimp_object_queue_push_list (queue, list);
251         }
252 
253       if (list->next)
254         {
255           gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_TRANSFORM,
256                                        C_("undo-type", "Transform Items"));
257 
258           for (l = list; l; l = g_list_next (l))
259             gimp_item_start_transform (GIMP_ITEM (l->data), TRUE);
260         }
261 
262       for (l = list; l; l = g_list_next (l))
263         {
264           GimpItem *item = l->data;
265 
266           if (queue)
267             gimp_object_queue_pop (queue);
268 
269           gimp_item_transform (item, context,
270                                matrix, direction,
271                                interpolation_type,
272                                gimp_item_get_clip (item, clip_result),
273                                progress);
274         }
275 
276       if (list->next)
277         {
278           for (l = list; l; l = g_list_next (l))
279             gimp_item_end_transform (GIMP_ITEM (l->data), TRUE);
280 
281           gimp_image_undo_group_end (image);
282         }
283 
284       g_clear_object (&queue);
285     }
286 }
287 
288 /**
289  * gimp_image_item_list_get_list:
290  * @image:   An @image.
291  * @type:    Which type of items to return.
292  * @set:     Set the returned items are part of.
293  *
294  * This function returns a #GList of #GimpItem<!-- -->s for which the
295  * @type and @set criterions match.
296  *
297  * Return value: The list of items.
298  **/
299 GList *
gimp_image_item_list_get_list(GimpImage * image,GimpItemTypeMask type,GimpItemSet set)300 gimp_image_item_list_get_list (GimpImage        *image,
301                                GimpItemTypeMask  type,
302                                GimpItemSet       set)
303 {
304   GList *all_items;
305   GList *list;
306   GList *return_list = NULL;
307 
308   g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL);
309 
310   if (type & GIMP_ITEM_TYPE_LAYERS)
311     {
312       all_items = gimp_image_get_layer_list (image);
313 
314       for (list = all_items; list; list = g_list_next (list))
315         {
316           GimpItem *item = list->data;
317 
318           if (gimp_item_is_in_set (item, set))
319             return_list = g_list_prepend (return_list, item);
320         }
321 
322       g_list_free (all_items);
323     }
324 
325   if (type & GIMP_ITEM_TYPE_CHANNELS)
326     {
327       all_items = gimp_image_get_channel_list (image);
328 
329       for (list = all_items; list; list = g_list_next (list))
330         {
331           GimpItem *item = list->data;
332 
333           if (gimp_item_is_in_set (item, set))
334             return_list = g_list_prepend (return_list, item);
335         }
336 
337       g_list_free (all_items);
338     }
339 
340   if (type & GIMP_ITEM_TYPE_VECTORS)
341     {
342       all_items = gimp_image_get_vectors_list (image);
343 
344       for (list = all_items; list; list = g_list_next (list))
345         {
346           GimpItem *item = list->data;
347 
348           if (gimp_item_is_in_set (item, set))
349             return_list = g_list_prepend (return_list, item);
350         }
351 
352       g_list_free (all_items);
353     }
354 
355   return g_list_reverse (return_list);
356 }
357 
358 static GList *
gimp_image_item_list_remove_children(GList * list,const GimpItem * parent)359 gimp_image_item_list_remove_children (GList          *list,
360                                       const GimpItem *parent)
361 {
362   GList *l = list;
363 
364   while (l)
365     {
366       GimpItem *item = l->data;
367 
368       l = g_list_next (l);
369 
370       if (gimp_viewable_is_ancestor (GIMP_VIEWABLE (parent),
371                                      GIMP_VIEWABLE (item)))
372         {
373           list = g_list_remove (list, item);
374         }
375     }
376 
377   return list;
378 }
379 
380 GList *
gimp_image_item_list_filter(GList * list)381 gimp_image_item_list_filter (GList *list)
382 {
383   GList *l;
384 
385   if (! list)
386     return NULL;
387 
388   for (l = list; l; l = g_list_next (l))
389     {
390       GimpItem *item = l->data;
391       GList    *next;
392 
393       next = gimp_image_item_list_remove_children (g_list_next (l), item);
394 
395       l->next = next;
396       if (next)
397         next->prev = l;
398     }
399 
400   return list;
401 }
402