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