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