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