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