Fix T59170: Box select ignores modifiers-keys once tool option is set
[blender.git] / source / blender / windowmanager / intern / wm_gesture_ops.c
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (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, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * The Original Code is Copyright (C) 2007 Blender Foundation.
19  * All rights reserved.
20  *
21  * Contributor(s): Blender Foundation
22  *
23  * ***** END GPL LICENSE BLOCK *****
24  */
25
26 /** \file blender/windowmanager/intern/wm_gesture_ops.c
27  *  \ingroup wm
28  *
29  * Default operator callbacks for use with gestures (border/circle/lasso/straightline).
30  * Operators themselves are defined elsewhere.
31  *
32  * - Keymaps are in ``wm_operators.c``.
33  * - Property definitions are in ``wm_operator_props.c``.
34  */
35
36 #include "MEM_guardedalloc.h"
37
38 #include "DNA_windowmanager_types.h"
39
40 #include "BLI_math.h"
41
42 #include "BKE_context.h"
43
44 #include "WM_types.h"
45 #include "WM_api.h"
46
47 #include "wm.h"
48 #include "wm_event_types.h"
49 #include "wm_event_system.h"
50
51 #include "ED_screen.h"
52 #include "ED_select_utils.h"
53
54 #include "RNA_access.h"
55 #include "RNA_define.h"
56
57 /* -------------------------------------------------------------------- */
58 /** \name Internal Gesture Utilities
59  *
60  * Border gesture has two types:
61  * -# #WM_GESTURE_CROSS_RECT: starts a cross, on mouse click it changes to border.
62  * -# #WM_GESTURE_RECT: starts immediate as a border, on mouse click or release it ends.
63  *
64  * It stores 4 values (xmin, xmax, ymin, ymax) and event it ended with (event_type).
65  *
66  * \{ */
67
68 static void gesture_modal_end(bContext *C, wmOperator *op)
69 {
70         wmGesture *gesture = op->customdata;
71
72         WM_gesture_end(C, gesture); /* frees gesture itself, and unregisters from window */
73         op->customdata = NULL;
74
75         ED_area_tag_redraw(CTX_wm_area(C));
76
77         if (RNA_struct_find_property(op->ptr, "cursor")) {
78                 WM_cursor_modal_restore(CTX_wm_window(C));
79         }
80 }
81
82 static void gesture_modal_state_to_operator(wmOperator *op, int modal_state)
83 {
84         PropertyRNA *prop;
85
86         switch (modal_state) {
87                 case GESTURE_MODAL_SELECT:
88                 case GESTURE_MODAL_DESELECT:
89                         if ((prop = RNA_struct_find_property(op->ptr, "deselect"))) {
90                                 RNA_property_boolean_set(op->ptr, prop, (modal_state == GESTURE_MODAL_DESELECT));
91                         }
92                         if ((prop = RNA_struct_find_property(op->ptr, "mode"))) {
93                                 RNA_property_enum_set(op->ptr, prop, (modal_state == GESTURE_MODAL_DESELECT) ? SEL_OP_SUB : SEL_OP_ADD);
94                         }
95                         break;
96                 case GESTURE_MODAL_IN:
97                 case GESTURE_MODAL_OUT:
98                         if ((prop = RNA_struct_find_property(op->ptr, "zoom_out"))) {
99                                 RNA_property_boolean_set(op->ptr, prop, (modal_state == GESTURE_MODAL_OUT));
100                         }
101                         break;
102         }
103 }
104
105 static int UNUSED_FUNCTION(gesture_modal_state_from_operator)(wmOperator *op)
106 {
107         PropertyRNA *prop;
108
109         if ((prop = RNA_struct_find_property(op->ptr, "deselect"))) {
110                 if (RNA_property_is_set(op->ptr, prop)) {
111                         return RNA_property_boolean_get(op->ptr, prop) ? GESTURE_MODAL_DESELECT : GESTURE_MODAL_SELECT;
112                 }
113         }
114         if ((prop = RNA_struct_find_property(op->ptr, "mode"))) {
115                 if (RNA_property_is_set(op->ptr, prop)) {
116                         return RNA_property_enum_get(op->ptr, prop) == SEL_OP_SUB ? GESTURE_MODAL_DESELECT : GESTURE_MODAL_SELECT;
117                 }
118         }
119         if ((prop = RNA_struct_find_property(op->ptr, "zoom_out"))) {
120                 if (RNA_property_is_set(op->ptr, prop)) {
121                         return RNA_property_boolean_get(op->ptr, prop) ? GESTURE_MODAL_OUT : GESTURE_MODAL_IN;
122                 }
123         }
124         return GESTURE_MODAL_NOP;
125 }
126 /** \} */
127
128
129 /* -------------------------------------------------------------------- */
130 /** \name Border Gesture
131  *
132  * Border gesture has two types:
133  * -# #WM_GESTURE_CROSS_RECT: starts a cross, on mouse click it changes to border.
134  * -# #WM_GESTURE_RECT: starts immediate as a border, on mouse click or release it ends.
135  *
136  * It stores 4 values (xmin, xmax, ymin, ymax) and event it ended with (event_type).
137  *
138  * \{ */
139
140 static bool gesture_box_apply_rect(wmOperator *op)
141 {
142         wmGesture *gesture = op->customdata;
143         rcti *rect = gesture->customdata;
144
145         if (rect->xmin == rect->xmax || rect->ymin == rect->ymax)
146                 return 0;
147
148
149         /* operator arguments and storage. */
150         RNA_int_set(op->ptr, "xmin", min_ii(rect->xmin, rect->xmax));
151         RNA_int_set(op->ptr, "ymin", min_ii(rect->ymin, rect->ymax));
152         RNA_int_set(op->ptr, "xmax", max_ii(rect->xmin, rect->xmax));
153         RNA_int_set(op->ptr, "ymax", max_ii(rect->ymin, rect->ymax));
154
155         return 1;
156 }
157
158 static bool gesture_box_apply(bContext *C, wmOperator *op)
159 {
160         wmGesture *gesture = op->customdata;
161
162         int retval;
163
164         if (!gesture_box_apply_rect(op)) {
165                 return 0;
166         }
167
168         if (gesture->wait_for_input) {
169                 gesture_modal_state_to_operator(op, gesture->modal_state);
170         }
171
172         retval = op->type->exec(C, op);
173         OPERATOR_RETVAL_CHECK(retval);
174
175         return 1;
176 }
177
178 int WM_gesture_box_invoke(bContext *C, wmOperator *op, const wmEvent *event)
179 {
180         const bool wait_for_input = !ISTWEAK(event->type) && RNA_boolean_get(op->ptr, "wait_for_input");
181         if (wait_for_input) {
182                 op->customdata = WM_gesture_new(C, event, WM_GESTURE_CROSS_RECT);
183         }
184         else {
185                 op->customdata = WM_gesture_new(C, event, WM_GESTURE_RECT);
186         }
187
188         {
189                 wmGesture *gesture = op->customdata;
190                 gesture->wait_for_input = wait_for_input;
191         }
192
193         /* add modal handler */
194         WM_event_add_modal_handler(C, op);
195
196         wm_gesture_tag_redraw(C);
197
198         return OPERATOR_RUNNING_MODAL;
199 }
200
201 int WM_gesture_box_modal(bContext *C, wmOperator *op, const wmEvent *event)
202 {
203         wmGesture *gesture = op->customdata;
204         rcti *rect = gesture->customdata;
205
206         if (event->type == MOUSEMOVE) {
207                 if (gesture->type == WM_GESTURE_CROSS_RECT && gesture->is_active == false) {
208                         rect->xmin = rect->xmax = event->x - gesture->winrct.xmin;
209                         rect->ymin = rect->ymax = event->y - gesture->winrct.ymin;
210                 }
211                 else {
212                         rect->xmax = event->x - gesture->winrct.xmin;
213                         rect->ymax = event->y - gesture->winrct.ymin;
214                 }
215                 gesture_box_apply_rect(op);
216
217                 wm_gesture_tag_redraw(C);
218         }
219         else if (event->type == EVT_MODAL_MAP) {
220                 switch (event->val) {
221                         case GESTURE_MODAL_BEGIN:
222                                 if (gesture->type == WM_GESTURE_CROSS_RECT && gesture->is_active == false) {
223                                         gesture->is_active = true;
224                                         wm_gesture_tag_redraw(C);
225                                 }
226                                 break;
227                         case GESTURE_MODAL_SELECT:
228                         case GESTURE_MODAL_DESELECT:
229                         case GESTURE_MODAL_IN:
230                         case GESTURE_MODAL_OUT:
231                                 if (gesture->wait_for_input) {
232                                         gesture->modal_state = event->val;
233                                 }
234                                 if (gesture_box_apply(C, op)) {
235                                         gesture_modal_end(C, op);
236                                         return OPERATOR_FINISHED;
237                                 }
238                                 gesture_modal_end(C, op);
239                                 return OPERATOR_CANCELLED;
240
241                         case GESTURE_MODAL_CANCEL:
242                                 gesture_modal_end(C, op);
243                                 return OPERATOR_CANCELLED;
244                 }
245
246         }
247 #ifdef WITH_INPUT_NDOF
248         else if (event->type == NDOF_MOTION) {
249                 return OPERATOR_PASS_THROUGH;
250         }
251 #endif
252
253 #if 0
254         /* Allow view navigation??? */
255         else {
256                 return OPERATOR_PASS_THROUGH;
257         }
258 #endif
259
260         return OPERATOR_RUNNING_MODAL;
261 }
262
263 void WM_gesture_box_cancel(bContext *C, wmOperator *op)
264 {
265         gesture_modal_end(C, op);
266 }
267
268 /** \} */
269
270
271 /* -------------------------------------------------------------------- */
272 /** \name Circle Gesture
273  *
274  * Currently only used for selection or modal paint stuff,
275  * calls ``exec`` while hold mouse, exits on release (with no difference between cancel and confirm).
276  *
277  * \{ */
278
279 static void gesture_circle_apply(bContext *C, wmOperator *op);
280
281 int WM_gesture_circle_invoke(bContext *C, wmOperator *op, const wmEvent *event)
282 {
283         const bool wait_for_input = !ISTWEAK(event->type) && RNA_boolean_get(op->ptr, "wait_for_input");
284
285         op->customdata = WM_gesture_new(C, event, WM_GESTURE_CIRCLE);
286         wmGesture *gesture = op->customdata;
287         rcti *rect = gesture->customdata;
288
289         /* Default or previously stored value. */
290         rect->xmax = RNA_int_get(op->ptr, "radius");
291
292         gesture->wait_for_input = wait_for_input;
293
294         /* Starting with the mode starts immediately, like having 'wait_for_input' disabled (some tools use this). */
295         if (gesture->wait_for_input == false) {
296                 gesture->is_active = true;
297                 gesture_circle_apply(C, op);
298         }
299
300         /* add modal handler */
301         WM_event_add_modal_handler(C, op);
302
303         wm_gesture_tag_redraw(C);
304
305         return OPERATOR_RUNNING_MODAL;
306 }
307
308 static void gesture_circle_apply(bContext *C, wmOperator *op)
309 {
310         wmGesture *gesture = op->customdata;
311         rcti *rect = gesture->customdata;
312
313         if (gesture->wait_for_input &&
314             (gesture->modal_state == GESTURE_MODAL_NOP))
315         {
316                 return;
317         }
318
319         /* operator arguments and storage. */
320         RNA_int_set(op->ptr, "x", rect->xmin);
321         RNA_int_set(op->ptr, "y", rect->ymin);
322         RNA_int_set(op->ptr, "radius", rect->xmax);
323
324         /* When 'wait_for_input' is false, use properties to get the selection state.
325          * typically tool settings. This is done so executing as a mode can select & de-select, see: T58594. */
326         if (gesture->wait_for_input) {
327                 gesture_modal_state_to_operator(op, gesture->modal_state);
328         }
329
330         if (op->type->exec) {
331                 int retval;
332                 retval = op->type->exec(C, op);
333                 OPERATOR_RETVAL_CHECK(retval);
334         }
335 }
336
337 int WM_gesture_circle_modal(bContext *C, wmOperator *op, const wmEvent *event)
338 {
339         wmGesture *gesture = op->customdata;
340         rcti *rect = gesture->customdata;
341
342         if (event->type == MOUSEMOVE) {
343
344                 rect->xmin = event->x - gesture->winrct.xmin;
345                 rect->ymin = event->y - gesture->winrct.ymin;
346
347                 wm_gesture_tag_redraw(C);
348
349                 if (gesture->is_active) {
350                         gesture_circle_apply(C, op);
351                 }
352         }
353         else if (event->type == EVT_MODAL_MAP) {
354                 bool is_circle_size = false;
355                 bool is_finished = false;
356                 float fac;
357
358                 switch (event->val) {
359                         case GESTURE_MODAL_CIRCLE_SIZE:
360                                 fac = 0.3f * (event->y - event->prevy);
361                                 if (fac > 0)
362                                         rect->xmax += ceil(fac);
363                                 else
364                                         rect->xmax += floor(fac);
365                                 if (rect->xmax < 1) rect->xmax = 1;
366                                 is_circle_size = true;
367                                 break;
368                         case GESTURE_MODAL_CIRCLE_ADD:
369                                 rect->xmax += 2 + rect->xmax / 10;
370                                 is_circle_size = true;
371                                 break;
372                         case GESTURE_MODAL_CIRCLE_SUB:
373                                 rect->xmax -= 2 + rect->xmax / 10;
374                                 if (rect->xmax < 1) rect->xmax = 1;
375                                 is_circle_size = true;
376                                 break;
377                         case GESTURE_MODAL_SELECT:
378                         case GESTURE_MODAL_DESELECT:
379                         case GESTURE_MODAL_NOP:
380                         {
381                                 if (gesture->wait_for_input) {
382                                         gesture->modal_state = event->val;
383                                 }
384                                 if (event->val == GESTURE_MODAL_NOP) {
385                                         /* Single action, click-drag & release to exit. */
386                                         if (gesture->wait_for_input == false) {
387                                                 is_finished = true;
388                                         }
389                                 }
390                                 else {
391                                         /* apply first click */
392                                         gesture_circle_apply(C, op);
393                                         gesture->is_active = true;
394                                         wm_gesture_tag_redraw(C);
395                                 }
396                                 break;
397                         }
398                         case GESTURE_MODAL_CANCEL:
399                         case GESTURE_MODAL_CONFIRM:
400                                 is_finished = true;
401                 }
402
403                 if (is_finished) {
404                         gesture_modal_end(C, op);
405                         return OPERATOR_FINISHED; /* use finish or we don't get an undo */
406                 }
407
408                 if (is_circle_size) {
409                         wm_gesture_tag_redraw(C);
410
411                         /* So next use remembers last seen size, even if we didn't apply it. */
412                         RNA_int_set(op->ptr, "radius", rect->xmax);
413                 }
414         }
415 #ifdef WITH_INPUT_NDOF
416         else if (event->type == NDOF_MOTION) {
417                 return OPERATOR_PASS_THROUGH;
418         }
419 #endif
420
421 #if 0
422         /* Allow view navigation??? */
423         /* note, this gives issues:
424          * 1) other modal ops run on top (box select),
425          * 2) middlemouse is used now 3) tablet/trackpad? */
426         else {
427                 return OPERATOR_PASS_THROUGH;
428         }
429 #endif
430
431
432         return OPERATOR_RUNNING_MODAL;
433 }
434
435 void WM_gesture_circle_cancel(bContext *C, wmOperator *op)
436 {
437         gesture_modal_end(C, op);
438 }
439
440 #if 0
441 /* template to copy from */
442 void WM_OT_circle_gesture(wmOperatorType *ot)
443 {
444         ot->name = "Circle Gesture";
445         ot->idname = "WM_OT_circle_gesture";
446         ot->description = "Enter rotate mode with a circular gesture";
447
448         ot->invoke = WM_gesture_circle_invoke;
449         ot->modal = WM_gesture_circle_modal;
450         ot->poll = WM_operator_winactive;
451
452         /* properties */
453         WM_operator_properties_gesture_circle(ot);
454
455 }
456 #endif
457
458 /** \} */
459
460
461 /* -------------------------------------------------------------------- */
462 /** \name Tweak Gesture
463  * \{ */
464
465 static void gesture_tweak_modal(bContext *C, const wmEvent *event)
466 {
467         wmWindow *window = CTX_wm_window(C);
468         wmGesture *gesture = window->tweak;
469         rcti *rect = gesture->customdata;
470         int val;
471
472         switch (event->type) {
473                 case MOUSEMOVE:
474                 case INBETWEEN_MOUSEMOVE:
475
476                         rect->xmax = event->x - gesture->winrct.xmin;
477                         rect->ymax = event->y - gesture->winrct.ymin;
478
479                         if ((val = wm_gesture_evaluate(gesture))) {
480                                 wmEvent tevent;
481
482                                 wm_event_init_from_window(window, &tevent);
483                                 /* We want to get coord from start of drag, not from point where it becomes a tweak event, see T40549 */
484                                 tevent.x = rect->xmin + gesture->winrct.xmin;
485                                 tevent.y = rect->ymin + gesture->winrct.ymin;
486                                 if (gesture->event_type == LEFTMOUSE)
487                                         tevent.type = EVT_TWEAK_L;
488                                 else if (gesture->event_type == RIGHTMOUSE)
489                                         tevent.type = EVT_TWEAK_R;
490                                 else
491                                         tevent.type = EVT_TWEAK_M;
492                                 tevent.val = val;
493                                 /* mouse coords! */
494
495                                 /* important we add immediately after this event, so future mouse releases
496                                  * (which may be in the queue already), are handled in order, see T44740 */
497                                 wm_event_add_ex(window, &tevent, event);
498
499                                 WM_gesture_end(C, gesture); /* frees gesture itself, and unregisters from window */
500                         }
501
502                         break;
503
504                 case LEFTMOUSE:
505                 case RIGHTMOUSE:
506                 case MIDDLEMOUSE:
507                         if (gesture->event_type == event->type) {
508                                 WM_gesture_end(C, gesture);
509
510                                 /* when tweak fails we should give the other keymap entries a chance */
511
512                                 /* XXX, assigning to readonly, BAD JUJU! */
513                                 ((wmEvent *)event)->val = KM_RELEASE;
514                         }
515                         break;
516                 default:
517                         if (!ISTIMER(event->type) && event->type != EVENT_NONE) {
518                                 WM_gesture_end(C, gesture);
519                         }
520                         break;
521         }
522 }
523
524 /* standard tweak, called after window handlers passed on event */
525 void wm_tweakevent_test(bContext *C, const wmEvent *event, int action)
526 {
527         wmWindow *win = CTX_wm_window(C);
528
529         if (win->tweak == NULL) {
530                 if (CTX_wm_region(C)) {
531                         if (event->val == KM_PRESS) {
532                                 if (ELEM(event->type, LEFTMOUSE, MIDDLEMOUSE, RIGHTMOUSE)) {
533                                         win->tweak = WM_gesture_new(C, event, WM_GESTURE_TWEAK);
534                                 }
535                         }
536                 }
537         }
538         else {
539                 /* no tweaks if event was handled */
540                 if ((action & WM_HANDLER_BREAK)) {
541                         WM_gesture_end(C, win->tweak);
542                 }
543                 else
544                         gesture_tweak_modal(C, event);
545         }
546 }
547
548 /** \} */
549
550
551 /* -------------------------------------------------------------------- */
552 /** \name Lasso Gesture
553  * \{ */
554
555 int WM_gesture_lasso_invoke(bContext *C, wmOperator *op, const wmEvent *event)
556 {
557         PropertyRNA *prop;
558
559         op->customdata = WM_gesture_new(C, event, WM_GESTURE_LASSO);
560
561         /* add modal handler */
562         WM_event_add_modal_handler(C, op);
563
564         wm_gesture_tag_redraw(C);
565
566         if ((prop = RNA_struct_find_property(op->ptr, "cursor"))) {
567                 WM_cursor_modal_set(CTX_wm_window(C), RNA_property_int_get(op->ptr, prop));
568         }
569
570         return OPERATOR_RUNNING_MODAL;
571 }
572
573 int WM_gesture_lines_invoke(bContext *C, wmOperator *op, const wmEvent *event)
574 {
575         PropertyRNA *prop;
576
577         op->customdata = WM_gesture_new(C, event, WM_GESTURE_LINES);
578
579         /* add modal handler */
580         WM_event_add_modal_handler(C, op);
581
582         wm_gesture_tag_redraw(C);
583
584         if ((prop = RNA_struct_find_property(op->ptr, "cursor"))) {
585                 WM_cursor_modal_set(CTX_wm_window(C), RNA_property_int_get(op->ptr, prop));
586         }
587
588         return OPERATOR_RUNNING_MODAL;
589 }
590
591
592 static void gesture_lasso_apply(bContext *C, wmOperator *op)
593 {
594         wmGesture *gesture = op->customdata;
595         PointerRNA itemptr;
596         float loc[2];
597         int i;
598         const short *lasso = gesture->customdata;
599
600         /* operator storage as path. */
601
602         RNA_collection_clear(op->ptr, "path");
603         for (i = 0; i < gesture->points; i++, lasso += 2) {
604                 loc[0] = lasso[0];
605                 loc[1] = lasso[1];
606                 RNA_collection_add(op->ptr, "path", &itemptr);
607                 RNA_float_set_array(&itemptr, "loc", loc);
608         }
609
610         gesture_modal_end(C, op);
611
612         if (op->type->exec) {
613                 int retval = op->type->exec(C, op);
614                 OPERATOR_RETVAL_CHECK(retval);
615         }
616 }
617
618 int WM_gesture_lasso_modal(bContext *C, wmOperator *op, const wmEvent *event)
619 {
620         wmGesture *gesture = op->customdata;
621
622         switch (event->type) {
623                 case MOUSEMOVE:
624                 case INBETWEEN_MOUSEMOVE:
625
626                         wm_gesture_tag_redraw(C);
627
628                         if (gesture->points == gesture->points_alloc) {
629                                 gesture->points_alloc *= 2;
630                                 gesture->customdata = MEM_reallocN(gesture->customdata, sizeof(short[2]) * gesture->points_alloc);
631                         }
632
633                         {
634                                 int x, y;
635                                 short *lasso = gesture->customdata;
636
637                                 lasso += (2 * gesture->points - 2);
638                                 x = (event->x - gesture->winrct.xmin - lasso[0]);
639                                 y = (event->y - gesture->winrct.ymin - lasso[1]);
640
641                                 /* make a simple distance check to get a smoother lasso
642                                  * add only when at least 2 pixels between this and previous location */
643                                 if ((x * x + y * y) > 4) {
644                                         lasso += 2;
645                                         lasso[0] = event->x - gesture->winrct.xmin;
646                                         lasso[1] = event->y - gesture->winrct.ymin;
647                                         gesture->points++;
648                                 }
649                         }
650                         break;
651
652                 case LEFTMOUSE:
653                 case MIDDLEMOUSE:
654                 case RIGHTMOUSE:
655                         if (event->val == KM_RELEASE) {   /* key release */
656                                 gesture_lasso_apply(C, op);
657                                 return OPERATOR_FINISHED;
658                         }
659                         break;
660                 case ESCKEY:
661                         gesture_modal_end(C, op);
662                         return OPERATOR_CANCELLED;
663         }
664         return OPERATOR_RUNNING_MODAL;
665 }
666
667 int WM_gesture_lines_modal(bContext *C, wmOperator *op, const wmEvent *event)
668 {
669         return WM_gesture_lasso_modal(C, op, event);
670 }
671
672 void WM_gesture_lasso_cancel(bContext *C, wmOperator *op)
673 {
674         gesture_modal_end(C, op);
675 }
676
677 void WM_gesture_lines_cancel(bContext *C, wmOperator *op)
678 {
679         gesture_modal_end(C, op);
680 }
681
682 /**
683  * helper function, we may want to add options for conversion to view space
684  *
685  * caller must free.
686  */
687 const int (*WM_gesture_lasso_path_to_array(bContext *UNUSED(C), wmOperator *op, int *mcords_tot))[2]
688 {
689         PropertyRNA *prop = RNA_struct_find_property(op->ptr, "path");
690         int (*mcords)[2] = NULL;
691         BLI_assert(prop != NULL);
692
693         if (prop) {
694                 const int len = RNA_property_collection_length(op->ptr, prop);
695
696                 if (len) {
697                         int i = 0;
698                         mcords = MEM_mallocN(sizeof(int) * 2 * len, __func__);
699
700                         RNA_PROP_BEGIN (op->ptr, itemptr, prop)
701                         {
702                                 float loc[2];
703
704                                 RNA_float_get_array(&itemptr, "loc", loc);
705                                 mcords[i][0] = (int)loc[0];
706                                 mcords[i][1] = (int)loc[1];
707                                 i++;
708                         }
709                         RNA_PROP_END;
710                 }
711                 *mcords_tot = len;
712         }
713         else {
714                 *mcords_tot = 0;
715         }
716
717         /* cast for 'const' */
718         return (const int (*)[2])mcords;
719 }
720
721 #if 0
722 /* template to copy from */
723
724 static int gesture_lasso_exec(bContext *C, wmOperator *op)
725 {
726         RNA_BEGIN (op->ptr, itemptr, "path")
727         {
728                 float loc[2];
729
730                 RNA_float_get_array(&itemptr, "loc", loc);
731                 printf("Location: %f %f\n", loc[0], loc[1]);
732         }
733         RNA_END;
734
735         return OPERATOR_FINISHED;
736 }
737
738 void WM_OT_lasso_gesture(wmOperatorType *ot)
739 {
740         PropertyRNA *prop;
741
742         ot->name = "Lasso Gesture";
743         ot->idname = "WM_OT_lasso_gesture";
744         ot->description = "Select objects within the lasso as you move the pointer";
745
746         ot->invoke = WM_gesture_lasso_invoke;
747         ot->modal = WM_gesture_lasso_modal;
748         ot->exec = gesture_lasso_exec;
749
750         ot->poll = WM_operator_winactive;
751
752         prop = RNA_def_property(ot->srna, "path", PROP_COLLECTION, PROP_NONE);
753         RNA_def_property_struct_runtime(prop, &RNA_OperatorMousePath);
754 }
755 #endif
756
757
758 /** \} */
759
760
761 /* -------------------------------------------------------------------- */
762 /** \name Straight Line Gesture
763  * \{ */
764
765 static bool gesture_straightline_apply(bContext *C, wmOperator *op)
766 {
767         wmGesture *gesture = op->customdata;
768         rcti *rect = gesture->customdata;
769
770         if (rect->xmin == rect->xmax && rect->ymin == rect->ymax)
771                 return 0;
772
773         /* operator arguments and storage. */
774         RNA_int_set(op->ptr, "xstart", rect->xmin);
775         RNA_int_set(op->ptr, "ystart", rect->ymin);
776         RNA_int_set(op->ptr, "xend", rect->xmax);
777         RNA_int_set(op->ptr, "yend", rect->ymax);
778
779         if (op->type->exec) {
780                 int retval = op->type->exec(C, op);
781                 OPERATOR_RETVAL_CHECK(retval);
782         }
783
784         return 1;
785 }
786
787 int WM_gesture_straightline_invoke(bContext *C, wmOperator *op, const wmEvent *event)
788 {
789         PropertyRNA *prop;
790
791         op->customdata = WM_gesture_new(C, event, WM_GESTURE_STRAIGHTLINE);
792
793         if (ISTWEAK(event->type)) {
794                 wmGesture *gesture = op->customdata;
795                 gesture->is_active = true;
796         }
797
798         /* add modal handler */
799         WM_event_add_modal_handler(C, op);
800
801         wm_gesture_tag_redraw(C);
802
803         if ((prop = RNA_struct_find_property(op->ptr, "cursor"))) {
804                 WM_cursor_modal_set(CTX_wm_window(C), RNA_property_int_get(op->ptr, prop));
805         }
806
807         return OPERATOR_RUNNING_MODAL;
808 }
809
810 int WM_gesture_straightline_modal(bContext *C, wmOperator *op, const wmEvent *event)
811 {
812         wmGesture *gesture = op->customdata;
813         rcti *rect = gesture->customdata;
814
815         if (event->type == MOUSEMOVE) {
816                 if (gesture->is_active == false) {
817                         rect->xmin = rect->xmax = event->x - gesture->winrct.xmin;
818                         rect->ymin = rect->ymax = event->y - gesture->winrct.ymin;
819                 }
820                 else {
821                         rect->xmax = event->x - gesture->winrct.xmin;
822                         rect->ymax = event->y - gesture->winrct.ymin;
823                         gesture_straightline_apply(C, op);
824                 }
825
826                 wm_gesture_tag_redraw(C);
827         }
828         else if (event->type == EVT_MODAL_MAP) {
829                 switch (event->val) {
830                         case GESTURE_MODAL_BEGIN:
831                                 if (gesture->is_active == false) {
832                                         gesture->is_active = true;
833                                         wm_gesture_tag_redraw(C);
834                                 }
835                                 break;
836                         case GESTURE_MODAL_SELECT:
837                                 if (gesture_straightline_apply(C, op)) {
838                                         gesture_modal_end(C, op);
839                                         return OPERATOR_FINISHED;
840                                 }
841                                 gesture_modal_end(C, op);
842                                 return OPERATOR_CANCELLED;
843
844                         case GESTURE_MODAL_CANCEL:
845                                 gesture_modal_end(C, op);
846                                 return OPERATOR_CANCELLED;
847                 }
848         }
849
850         return OPERATOR_RUNNING_MODAL;
851 }
852
853 void WM_gesture_straightline_cancel(bContext *C, wmOperator *op)
854 {
855         gesture_modal_end(C, op);
856 }
857
858 #if 0
859 /* template to copy from */
860 void WM_OT_straightline_gesture(wmOperatorType *ot)
861 {
862         PropertyRNA *prop;
863
864         ot->name = "Straight Line Gesture";
865         ot->idname = "WM_OT_straightline_gesture";
866         ot->description = "Draw a straight line as you move the pointer";
867
868         ot->invoke = WM_gesture_straightline_invoke;
869         ot->modal = WM_gesture_straightline_modal;
870         ot->exec = gesture_straightline_exec;
871
872         ot->poll = WM_operator_winactive;
873
874         WM_operator_properties_gesture_straightline(ot, 0);
875 }
876 #endif
877
878 /** \} */