1 /* GIMP - The GNU Image Manipulation Program
2 * Copyright (C) 1995 Spencer Kimball and Peter Mattis
3 *
4 * Major improvement to support polygonal segments
5 * Copyright (C) 2008 Martin Nordholts
6 *
7 * This program is polygon software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Polygon 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 "tools-types.h"
29
30 #include "core/gimpchannel.h"
31 #include "core/gimpchannel-select.h"
32 #include "core/gimpimage.h"
33 #include "core/gimplayer-floating-selection.h"
34
35 #include "widgets/gimphelp-ids.h"
36 #include "widgets/gimpwidgets-utils.h"
37
38 #include "display/gimpdisplay.h"
39 #include "display/gimptoolpolygon.h"
40
41 #include "gimppolygonselecttool.h"
42 #include "gimpselectionoptions.h"
43 #include "gimptoolcontrol.h"
44
45
46 struct _GimpPolygonSelectToolPrivate
47 {
48 GimpToolWidget *widget;
49 GimpToolWidget *grab_widget;
50
51 gboolean pending_response;
52 gint pending_response_id;
53 };
54
55
56 /* local function prototypes */
57
58 static void gimp_polygon_select_tool_finalize (GObject *object);
59
60 static void gimp_polygon_select_tool_control (GimpTool *tool,
61 GimpToolAction action,
62 GimpDisplay *display);
63 static void gimp_polygon_select_tool_button_press (GimpTool *tool,
64 const GimpCoords *coords,
65 guint32 time,
66 GdkModifierType state,
67 GimpButtonPressType press_type,
68 GimpDisplay *display);
69 static void gimp_polygon_select_tool_button_release (GimpTool *tool,
70 const GimpCoords *coords,
71 guint32 time,
72 GdkModifierType state,
73 GimpButtonReleaseType release_type,
74 GimpDisplay *display);
75 static void gimp_polygon_select_tool_motion (GimpTool *tool,
76 const GimpCoords *coords,
77 guint32 time,
78 GdkModifierType state,
79 GimpDisplay *display);
80 static gboolean gimp_polygon_select_tool_key_press (GimpTool *tool,
81 GdkEventKey *kevent,
82 GimpDisplay *display);
83 static void gimp_polygon_select_tool_modifier_key (GimpTool *tool,
84 GdkModifierType key,
85 gboolean press,
86 GdkModifierType state,
87 GimpDisplay *display);
88 static void gimp_polygon_select_tool_oper_update (GimpTool *tool,
89 const GimpCoords *coords,
90 GdkModifierType state,
91 gboolean proximity,
92 GimpDisplay *display);
93 static void gimp_polygon_select_tool_cursor_update (GimpTool *tool,
94 const GimpCoords *coords,
95 GdkModifierType state,
96 GimpDisplay *display);
97
98 static void gimp_polygon_select_tool_real_confirm (GimpPolygonSelectTool *poly_sel,
99 GimpDisplay *display);
100
101 static void gimp_polygon_select_tool_polygon_change_complete (GimpToolWidget *polygon,
102 GimpPolygonSelectTool *poly_sel);
103 static void gimp_polygon_select_tool_polygon_response (GimpToolWidget *polygon,
104 gint response_id,
105 GimpPolygonSelectTool *poly_sel);
106
107 static void gimp_polygon_select_tool_start (GimpPolygonSelectTool *poly_sel,
108 GimpDisplay *display);
109
110
G_DEFINE_TYPE_WITH_PRIVATE(GimpPolygonSelectTool,gimp_polygon_select_tool,GIMP_TYPE_SELECTION_TOOL)111 G_DEFINE_TYPE_WITH_PRIVATE (GimpPolygonSelectTool, gimp_polygon_select_tool,
112 GIMP_TYPE_SELECTION_TOOL)
113
114 #define parent_class gimp_polygon_select_tool_parent_class
115
116
117 /* private functions */
118
119 static void
120 gimp_polygon_select_tool_class_init (GimpPolygonSelectToolClass *klass)
121 {
122 GObjectClass *object_class = G_OBJECT_CLASS (klass);
123 GimpToolClass *tool_class = GIMP_TOOL_CLASS (klass);
124
125 object_class->finalize = gimp_polygon_select_tool_finalize;
126
127 tool_class->control = gimp_polygon_select_tool_control;
128 tool_class->button_press = gimp_polygon_select_tool_button_press;
129 tool_class->button_release = gimp_polygon_select_tool_button_release;
130 tool_class->motion = gimp_polygon_select_tool_motion;
131 tool_class->key_press = gimp_polygon_select_tool_key_press;
132 tool_class->modifier_key = gimp_polygon_select_tool_modifier_key;
133 tool_class->oper_update = gimp_polygon_select_tool_oper_update;
134 tool_class->cursor_update = gimp_polygon_select_tool_cursor_update;
135
136 klass->change_complete = NULL;
137 klass->confirm = gimp_polygon_select_tool_real_confirm;
138 }
139
140 static void
gimp_polygon_select_tool_init(GimpPolygonSelectTool * poly_sel)141 gimp_polygon_select_tool_init (GimpPolygonSelectTool *poly_sel)
142 {
143 GimpTool *tool = GIMP_TOOL (poly_sel);
144 GimpSelectionTool *sel_tool = GIMP_SELECTION_TOOL (tool);
145
146 poly_sel->priv = gimp_polygon_select_tool_get_instance_private (poly_sel);
147
148 gimp_tool_control_set_motion_mode (tool->control,
149 GIMP_MOTION_MODE_EXACT);
150 gimp_tool_control_set_wants_click (tool->control, TRUE);
151 gimp_tool_control_set_wants_double_click (tool->control, TRUE);
152 gimp_tool_control_set_active_modifiers (tool->control,
153 GIMP_TOOL_ACTIVE_MODIFIERS_SEPARATE);
154 gimp_tool_control_set_precision (tool->control,
155 GIMP_CURSOR_PRECISION_SUBPIXEL);
156
157 sel_tool->allow_move = FALSE;
158 }
159
160 static void
gimp_polygon_select_tool_finalize(GObject * object)161 gimp_polygon_select_tool_finalize (GObject *object)
162 {
163 GimpPolygonSelectTool *poly_sel = GIMP_POLYGON_SELECT_TOOL (object);
164 GimpPolygonSelectToolPrivate *priv = poly_sel->priv;
165
166 g_clear_object (&priv->widget);
167
168 G_OBJECT_CLASS (parent_class)->finalize (object);
169 }
170
171 static void
gimp_polygon_select_tool_control(GimpTool * tool,GimpToolAction action,GimpDisplay * display)172 gimp_polygon_select_tool_control (GimpTool *tool,
173 GimpToolAction action,
174 GimpDisplay *display)
175 {
176 GimpPolygonSelectTool *poly_sel = GIMP_POLYGON_SELECT_TOOL (tool);
177
178 switch (action)
179 {
180 case GIMP_TOOL_ACTION_PAUSE:
181 case GIMP_TOOL_ACTION_RESUME:
182 break;
183
184 case GIMP_TOOL_ACTION_HALT:
185 gimp_polygon_select_tool_halt (poly_sel);
186 break;
187
188 case GIMP_TOOL_ACTION_COMMIT:
189 break;
190 }
191
192 GIMP_TOOL_CLASS (parent_class)->control (tool, action, display);
193 }
194
195 static void
gimp_polygon_select_tool_button_press(GimpTool * tool,const GimpCoords * coords,guint32 time,GdkModifierType state,GimpButtonPressType press_type,GimpDisplay * display)196 gimp_polygon_select_tool_button_press (GimpTool *tool,
197 const GimpCoords *coords,
198 guint32 time,
199 GdkModifierType state,
200 GimpButtonPressType press_type,
201 GimpDisplay *display)
202 {
203 GimpPolygonSelectTool *poly_sel = GIMP_POLYGON_SELECT_TOOL (tool);
204 GimpPolygonSelectToolPrivate *priv = poly_sel->priv;
205
206 if (tool->display && tool->display != display)
207 gimp_tool_control (tool, GIMP_TOOL_ACTION_COMMIT, tool->display);
208
209 if (! priv->widget) /* not tool->display, we have a subclass */
210 {
211 /* First of all handle delegation to the selection mask edit logic
212 * if appropriate.
213 */
214 if (gimp_selection_tool_start_edit (GIMP_SELECTION_TOOL (poly_sel),
215 display, coords))
216 {
217 return;
218 }
219
220 gimp_polygon_select_tool_start (poly_sel, display);
221
222 gimp_tool_widget_hover (priv->widget, coords, state, TRUE);
223 }
224
225 if (gimp_tool_widget_button_press (priv->widget, coords, time, state,
226 press_type))
227 {
228 priv->grab_widget = priv->widget;
229 }
230
231 if (press_type == GIMP_BUTTON_PRESS_NORMAL)
232 gimp_tool_control_activate (tool->control);
233 }
234
235 static void
gimp_polygon_select_tool_button_release(GimpTool * tool,const GimpCoords * coords,guint32 time,GdkModifierType state,GimpButtonReleaseType release_type,GimpDisplay * display)236 gimp_polygon_select_tool_button_release (GimpTool *tool,
237 const GimpCoords *coords,
238 guint32 time,
239 GdkModifierType state,
240 GimpButtonReleaseType release_type,
241 GimpDisplay *display)
242 {
243 GimpPolygonSelectTool *poly_sel = GIMP_POLYGON_SELECT_TOOL (tool);
244 GimpPolygonSelectToolPrivate *priv = poly_sel->priv;
245 GimpImage *image = gimp_display_get_image (display);
246
247 gimp_tool_control_halt (tool->control);
248
249 switch (release_type)
250 {
251 case GIMP_BUTTON_RELEASE_CLICK:
252 case GIMP_BUTTON_RELEASE_NO_MOTION:
253 /* If there is a floating selection, anchor it */
254 if (gimp_image_get_floating_selection (image))
255 {
256 floating_sel_anchor (gimp_image_get_floating_selection (image));
257
258 gimp_tool_control (tool, GIMP_TOOL_ACTION_HALT, display);
259
260 return;
261 }
262
263 /* fallthru */
264
265 default:
266 if (priv->grab_widget)
267 {
268 gimp_tool_widget_button_release (priv->grab_widget,
269 coords, time, state, release_type);
270 priv->grab_widget = NULL;
271 }
272 }
273
274 if (priv->pending_response)
275 {
276 gimp_polygon_select_tool_polygon_response (priv->widget,
277 priv->pending_response_id,
278 poly_sel);
279
280 priv->pending_response = FALSE;
281 }
282 }
283
284 static void
gimp_polygon_select_tool_motion(GimpTool * tool,const GimpCoords * coords,guint32 time,GdkModifierType state,GimpDisplay * display)285 gimp_polygon_select_tool_motion (GimpTool *tool,
286 const GimpCoords *coords,
287 guint32 time,
288 GdkModifierType state,
289 GimpDisplay *display)
290 {
291 GimpPolygonSelectTool *poly_sel = GIMP_POLYGON_SELECT_TOOL (tool);
292 GimpPolygonSelectToolPrivate *priv = poly_sel->priv;
293
294 if (priv->grab_widget)
295 {
296 gimp_tool_widget_motion (priv->grab_widget, coords, time, state);
297 }
298 }
299
300 static gboolean
gimp_polygon_select_tool_key_press(GimpTool * tool,GdkEventKey * kevent,GimpDisplay * display)301 gimp_polygon_select_tool_key_press (GimpTool *tool,
302 GdkEventKey *kevent,
303 GimpDisplay *display)
304 {
305 GimpPolygonSelectTool *poly_sel = GIMP_POLYGON_SELECT_TOOL (tool);
306 GimpPolygonSelectToolPrivate *priv = poly_sel->priv;
307
308 if (priv->widget && display == tool->display)
309 {
310 return gimp_tool_widget_key_press (priv->widget, kevent);
311 }
312
313 return FALSE;
314 }
315
316 static void
gimp_polygon_select_tool_modifier_key(GimpTool * tool,GdkModifierType key,gboolean press,GdkModifierType state,GimpDisplay * display)317 gimp_polygon_select_tool_modifier_key (GimpTool *tool,
318 GdkModifierType key,
319 gboolean press,
320 GdkModifierType state,
321 GimpDisplay *display)
322 {
323 GimpPolygonSelectTool *poly_sel = GIMP_POLYGON_SELECT_TOOL (tool);
324 GimpPolygonSelectToolPrivate *priv = poly_sel->priv;
325
326 if (priv->widget && display == tool->display)
327 {
328 gimp_tool_widget_hover_modifier (priv->widget, key, press, state);
329
330 /* let GimpSelectTool handle alt+<mod> */
331 if (! (state & GDK_MOD1_MASK))
332 {
333 /* otherwise, shift/ctrl are handled by the widget */
334 state &= ~(gimp_get_extend_selection_mask () |
335 gimp_get_modify_selection_mask ());
336 }
337 }
338
339 GIMP_TOOL_CLASS (parent_class)->modifier_key (tool, key, press, state,
340 display);
341 }
342
343 static void
gimp_polygon_select_tool_oper_update(GimpTool * tool,const GimpCoords * coords,GdkModifierType state,gboolean proximity,GimpDisplay * display)344 gimp_polygon_select_tool_oper_update (GimpTool *tool,
345 const GimpCoords *coords,
346 GdkModifierType state,
347 gboolean proximity,
348 GimpDisplay *display)
349 {
350 GimpPolygonSelectTool *poly_sel = GIMP_POLYGON_SELECT_TOOL (tool);
351 GimpPolygonSelectToolPrivate *priv = poly_sel->priv;
352
353 if (priv->widget && display == tool->display)
354 {
355 gimp_tool_widget_hover (priv->widget, coords, state, proximity);
356
357 /* let GimpSelectTool handle alt+<mod> */
358 if (! (state & GDK_MOD1_MASK))
359 {
360 /* otherwise, shift/ctrl are handled by the widget */
361 state &= ~(gimp_get_extend_selection_mask () |
362 gimp_get_modify_selection_mask ());
363 }
364 }
365
366 GIMP_TOOL_CLASS (parent_class)->oper_update (tool, coords, state, proximity,
367 display);
368 }
369
370 static void
gimp_polygon_select_tool_cursor_update(GimpTool * tool,const GimpCoords * coords,GdkModifierType state,GimpDisplay * display)371 gimp_polygon_select_tool_cursor_update (GimpTool *tool,
372 const GimpCoords *coords,
373 GdkModifierType state,
374 GimpDisplay *display)
375 {
376 GimpPolygonSelectTool *poly_sel = GIMP_POLYGON_SELECT_TOOL (tool);
377 GimpPolygonSelectToolPrivate *priv = poly_sel->priv;
378 GimpCursorModifier modifier = GIMP_CURSOR_MODIFIER_NONE;
379
380 if (tool->display)
381 {
382 if (priv->widget && display == tool->display)
383 {
384 gimp_tool_widget_get_cursor (priv->widget, coords, state,
385 NULL, NULL, &modifier);
386
387 /* let GimpSelectTool handle alt+<mod> */
388 if (! (state & GDK_MOD1_MASK))
389 {
390 /* otherwise, shift/ctrl are handled by the widget */
391 state &= ~(gimp_get_extend_selection_mask () |
392 gimp_get_modify_selection_mask ());
393 }
394 }
395
396 gimp_tool_set_cursor (tool, display,
397 gimp_tool_control_get_cursor (tool->control),
398 gimp_tool_control_get_tool_cursor (tool->control),
399 modifier);
400 }
401
402 GIMP_TOOL_CLASS (parent_class)->cursor_update (tool, coords, state,
403 display);
404 }
405
406 static void
gimp_polygon_select_tool_real_confirm(GimpPolygonSelectTool * poly_sel,GimpDisplay * display)407 gimp_polygon_select_tool_real_confirm (GimpPolygonSelectTool *poly_sel,
408 GimpDisplay *display)
409 {
410 gimp_tool_control (GIMP_TOOL (poly_sel), GIMP_TOOL_ACTION_COMMIT, display);
411 }
412
413 static void
gimp_polygon_select_tool_polygon_change_complete(GimpToolWidget * polygon,GimpPolygonSelectTool * poly_sel)414 gimp_polygon_select_tool_polygon_change_complete (GimpToolWidget *polygon,
415 GimpPolygonSelectTool *poly_sel)
416 {
417 if (GIMP_POLYGON_SELECT_TOOL_GET_CLASS (poly_sel)->change_complete)
418 {
419 GIMP_POLYGON_SELECT_TOOL_GET_CLASS (poly_sel)->change_complete (
420 poly_sel, GIMP_TOOL (poly_sel)->display);
421 }
422 }
423
424 static void
gimp_polygon_select_tool_polygon_response(GimpToolWidget * polygon,gint response_id,GimpPolygonSelectTool * poly_sel)425 gimp_polygon_select_tool_polygon_response (GimpToolWidget *polygon,
426 gint response_id,
427 GimpPolygonSelectTool *poly_sel)
428 {
429 GimpTool *tool = GIMP_TOOL (poly_sel);
430 GimpPolygonSelectToolPrivate *priv = poly_sel->priv;
431
432 /* if we're in the middle of a click, defer the response to the
433 * button_release() event
434 */
435 if (gimp_tool_control_is_active (tool->control))
436 {
437 priv->pending_response = TRUE;
438 priv->pending_response_id = response_id;
439
440 return;
441 }
442
443 switch (response_id)
444 {
445 case GIMP_TOOL_WIDGET_RESPONSE_CONFIRM:
446 /* don't gimp_tool_control(COMMIT) here because we don't always
447 * want to HALT the tool after committing because we have a
448 * subclass, we do that in the default implementation of
449 * confirm().
450 */
451 if (GIMP_POLYGON_SELECT_TOOL_GET_CLASS (poly_sel)->confirm)
452 {
453 GIMP_POLYGON_SELECT_TOOL_GET_CLASS (poly_sel)->confirm (
454 poly_sel, tool->display);
455 }
456 break;
457
458 case GIMP_TOOL_WIDGET_RESPONSE_CANCEL:
459 gimp_tool_control (tool, GIMP_TOOL_ACTION_HALT, tool->display);
460 break;
461 }
462 }
463
464 static void
gimp_polygon_select_tool_start(GimpPolygonSelectTool * poly_sel,GimpDisplay * display)465 gimp_polygon_select_tool_start (GimpPolygonSelectTool *poly_sel,
466 GimpDisplay *display)
467 {
468 GimpTool *tool = GIMP_TOOL (poly_sel);
469 GimpPolygonSelectToolPrivate *priv = poly_sel->priv;
470 GimpDisplayShell *shell = gimp_display_get_shell (display);
471
472 tool->display = display;
473
474 priv->widget = gimp_tool_polygon_new (shell);
475
476 gimp_draw_tool_set_widget (GIMP_DRAW_TOOL (tool), priv->widget);
477
478 g_signal_connect (priv->widget, "change-complete",
479 G_CALLBACK (gimp_polygon_select_tool_polygon_change_complete),
480 poly_sel);
481 g_signal_connect (priv->widget, "response",
482 G_CALLBACK (gimp_polygon_select_tool_polygon_response),
483 poly_sel);
484
485 gimp_draw_tool_start (GIMP_DRAW_TOOL (tool), display);
486 }
487
488
489 /* public functions */
490
491 gboolean
gimp_polygon_select_tool_is_closed(GimpPolygonSelectTool * poly_sel)492 gimp_polygon_select_tool_is_closed (GimpPolygonSelectTool *poly_sel)
493 {
494 GimpPolygonSelectToolPrivate *priv;
495
496 g_return_val_if_fail (GIMP_IS_POLYGON_SELECT_TOOL (poly_sel), FALSE);
497
498 priv = poly_sel->priv;
499
500 if (priv->widget)
501 return gimp_tool_polygon_is_closed (GIMP_TOOL_POLYGON (priv->widget));
502
503 return FALSE;
504 }
505
506 void
gimp_polygon_select_tool_get_points(GimpPolygonSelectTool * poly_sel,const GimpVector2 ** points,gint * n_points)507 gimp_polygon_select_tool_get_points (GimpPolygonSelectTool *poly_sel,
508 const GimpVector2 **points,
509 gint *n_points)
510 {
511 GimpPolygonSelectToolPrivate *priv;
512
513 g_return_if_fail (GIMP_IS_POLYGON_SELECT_TOOL (poly_sel));
514
515 priv = poly_sel->priv;
516
517 if (priv->widget)
518 {
519 gimp_tool_polygon_get_points (GIMP_TOOL_POLYGON (priv->widget),
520 points, n_points);
521 }
522 else
523 {
524 if (points) *points = NULL;
525 if (n_points) *n_points = 0;
526 }
527 }
528
529
530 /* protected functions */
531
532 gboolean
gimp_polygon_select_tool_is_grabbed(GimpPolygonSelectTool * poly_sel)533 gimp_polygon_select_tool_is_grabbed (GimpPolygonSelectTool *poly_sel)
534 {
535 GimpPolygonSelectToolPrivate *priv;
536
537 g_return_val_if_fail (GIMP_IS_POLYGON_SELECT_TOOL (poly_sel), FALSE);
538
539 priv = poly_sel->priv;
540
541 return priv->grab_widget != NULL;
542 }
543
544 void
gimp_polygon_select_tool_halt(GimpPolygonSelectTool * poly_sel)545 gimp_polygon_select_tool_halt (GimpPolygonSelectTool *poly_sel)
546 {
547 GimpPolygonSelectToolPrivate *priv;
548
549 g_return_if_fail (GIMP_IS_POLYGON_SELECT_TOOL (poly_sel));
550
551 priv = poly_sel->priv;
552
553 gimp_draw_tool_set_widget (GIMP_DRAW_TOOL (poly_sel), NULL);
554 g_clear_object (&priv->widget);
555 }
556