Cleanup: comment line length (windowmanager)
[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 #wmOperator.exec while hold mouse, exits on release
271  * (with no difference between cancel and confirm).
272  *
273  * \{ */
274
275 static void gesture_circle_apply(bContext *C, wmOperator *op);
276
277 int WM_gesture_circle_invoke(bContext *C, wmOperator *op, const wmEvent *event)
278 {
279   const bool wait_for_input = !ISTWEAK(event->type) && RNA_boolean_get(op->ptr, "wait_for_input");
280
281   op->customdata = WM_gesture_new(C, event, WM_GESTURE_CIRCLE);
282   wmGesture *gesture = op->customdata;
283   rcti *rect = gesture->customdata;
284
285   /* Default or previously stored value. */
286   rect->xmax = RNA_int_get(op->ptr, "radius");
287
288   gesture->wait_for_input = wait_for_input;
289
290   /* Starting with the mode starts immediately,
291    * like having 'wait_for_input' disabled (some tools use this). */
292   if (gesture->wait_for_input == false) {
293     gesture->is_active = true;
294     gesture_circle_apply(C, op);
295   }
296
297   /* add modal handler */
298   WM_event_add_modal_handler(C, op);
299
300   wm_gesture_tag_redraw(C);
301
302   return OPERATOR_RUNNING_MODAL;
303 }
304
305 static void gesture_circle_apply(bContext *C, wmOperator *op)
306 {
307   wmGesture *gesture = op->customdata;
308   rcti *rect = gesture->customdata;
309
310   if (gesture->wait_for_input && (gesture->modal_state == GESTURE_MODAL_NOP)) {
311     return;
312   }
313
314   /* operator arguments and storage. */
315   RNA_int_set(op->ptr, "x", rect->xmin);
316   RNA_int_set(op->ptr, "y", rect->ymin);
317   RNA_int_set(op->ptr, "radius", rect->xmax);
318
319   /* When 'wait_for_input' is false,
320    * use properties to get the selection state (typically tool settings).
321    * This is done so executing as a mode can select & de-select, see: T58594. */
322   if (gesture->wait_for_input) {
323     gesture_modal_state_to_operator(op, gesture->modal_state);
324   }
325
326   if (op->type->exec) {
327     int retval;
328     retval = op->type->exec(C, op);
329     OPERATOR_RETVAL_CHECK(retval);
330   }
331 }
332
333 int WM_gesture_circle_modal(bContext *C, wmOperator *op, const wmEvent *event)
334 {
335   wmGesture *gesture = op->customdata;
336   rcti *rect = gesture->customdata;
337
338   if (event->type == MOUSEMOVE) {
339
340     rect->xmin = event->x - gesture->winrct.xmin;
341     rect->ymin = event->y - gesture->winrct.ymin;
342
343     wm_gesture_tag_redraw(C);
344
345     if (gesture->is_active) {
346       gesture_circle_apply(C, op);
347     }
348   }
349   else if (event->type == EVT_MODAL_MAP) {
350     bool is_circle_size = false;
351     bool is_finished = false;
352     float fac;
353
354     switch (event->val) {
355       case GESTURE_MODAL_CIRCLE_SIZE:
356         fac = 0.3f * (event->y - event->prevy);
357         if (fac > 0) {
358           rect->xmax += ceil(fac);
359         }
360         else {
361           rect->xmax += floor(fac);
362         }
363         if (rect->xmax < 1) {
364           rect->xmax = 1;
365         }
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) {
375           rect->xmax = 1;
376         }
377         is_circle_size = true;
378         break;
379       case GESTURE_MODAL_SELECT:
380       case GESTURE_MODAL_DESELECT:
381       case GESTURE_MODAL_NOP: {
382         if (gesture->wait_for_input) {
383           gesture->modal_state = event->val;
384         }
385         if (event->val == GESTURE_MODAL_NOP) {
386           /* Single action, click-drag & release to exit. */
387           if (gesture->wait_for_input == false) {
388             is_finished = true;
389           }
390         }
391         else {
392           /* apply first click */
393           gesture->is_active = true;
394           gesture_circle_apply(C, op);
395           wm_gesture_tag_redraw(C);
396         }
397         break;
398       }
399       case GESTURE_MODAL_CANCEL:
400       case GESTURE_MODAL_CONFIRM:
401         is_finished = true;
402     }
403
404     if (is_finished) {
405       gesture_modal_end(C, op);
406       return OPERATOR_FINISHED; /* use finish or we don't get an undo */
407     }
408
409     if (is_circle_size) {
410       wm_gesture_tag_redraw(C);
411
412       /* So next use remembers last seen size, even if we didn't apply it. */
413       RNA_int_set(op->ptr, "radius", rect->xmax);
414     }
415   }
416 #ifdef WITH_INPUT_NDOF
417   else if (event->type == NDOF_MOTION) {
418     return OPERATOR_PASS_THROUGH;
419   }
420 #endif
421
422 #if 0
423   /* Allow view navigation??? */
424   /* note, this gives issues:
425    * 1) other modal ops run on top (box select),
426    * 2) middlemouse is used now 3) tablet/trackpad? */
427   else {
428     return OPERATOR_PASS_THROUGH;
429   }
430 #endif
431
432   gesture->is_active_prev = gesture->is_active;
433   return OPERATOR_RUNNING_MODAL;
434 }
435
436 void WM_gesture_circle_cancel(bContext *C, wmOperator *op)
437 {
438   gesture_modal_end(C, op);
439 }
440
441 #if 0
442 /* template to copy from */
443 void WM_OT_circle_gesture(wmOperatorType *ot)
444 {
445   ot->name = "Circle Gesture";
446   ot->idname = "WM_OT_circle_gesture";
447   ot->description = "Enter rotate mode with a circular gesture";
448
449   ot->invoke = WM_gesture_circle_invoke;
450   ot->modal = WM_gesture_circle_modal;
451   ot->poll = WM_operator_winactive;
452
453   /* properties */
454   WM_operator_properties_gesture_circle(ot);
455 }
456 #endif
457
458 /** \} */
459
460 /* -------------------------------------------------------------------- */
461 /** \name Tweak Gesture
462  * \{ */
463
464 static void gesture_tweak_modal(bContext *C, const wmEvent *event)
465 {
466   wmWindow *window = CTX_wm_window(C);
467   wmGesture *gesture = window->tweak;
468   rcti *rect = gesture->customdata;
469   int val;
470
471   switch (event->type) {
472     case MOUSEMOVE:
473     case INBETWEEN_MOUSEMOVE:
474
475       rect->xmax = event->x - gesture->winrct.xmin;
476       rect->ymax = event->y - gesture->winrct.ymin;
477
478       if ((val = wm_gesture_evaluate(gesture))) {
479         wmEvent tevent;
480
481         wm_event_init_from_window(window, &tevent);
482         /* We want to get coord from start of drag,
483          * 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         }
489         else if (gesture->event_type == RIGHTMOUSE) {
490           tevent.type = EVT_TWEAK_R;
491         }
492         else {
493           tevent.type = EVT_TWEAK_M;
494         }
495         tevent.val = val;
496         /* mouse coords! */
497
498         /* important we add immediately after this event, so future mouse releases
499          * (which may be in the queue already), are handled in order, see T44740 */
500         wm_event_add_ex(window, &tevent, event);
501
502         WM_gesture_end(C, gesture); /* frees gesture itself, and unregisters from window */
503       }
504
505       break;
506
507     case LEFTMOUSE:
508     case RIGHTMOUSE:
509     case MIDDLEMOUSE:
510       if (gesture->event_type == event->type) {
511         WM_gesture_end(C, gesture);
512
513         /* when tweak fails we should give the other keymap entries a chance */
514
515         /* XXX, assigning to readonly, BAD JUJU! */
516         ((wmEvent *)event)->val = KM_RELEASE;
517       }
518       break;
519     default:
520       if (!ISTIMER(event->type) && event->type != EVENT_NONE) {
521         WM_gesture_end(C, gesture);
522       }
523       break;
524   }
525 }
526
527 /* standard tweak, called after window handlers passed on event */
528 void wm_tweakevent_test(bContext *C, const wmEvent *event, int action)
529 {
530   wmWindow *win = CTX_wm_window(C);
531
532   if (win->tweak == NULL) {
533     if (CTX_wm_region(C)) {
534       if (event->val == KM_PRESS) {
535         if (ELEM(event->type, LEFTMOUSE, MIDDLEMOUSE, RIGHTMOUSE)) {
536           win->tweak = WM_gesture_new(C, event, WM_GESTURE_TWEAK);
537         }
538       }
539     }
540   }
541   else {
542     /* no tweaks if event was handled */
543     if ((action & WM_HANDLER_BREAK)) {
544       WM_gesture_end(C, win->tweak);
545     }
546     else {
547       gesture_tweak_modal(C, event);
548     }
549   }
550 }
551
552 /** \} */
553
554 /* -------------------------------------------------------------------- */
555 /** \name Lasso Gesture
556  * \{ */
557
558 int WM_gesture_lasso_invoke(bContext *C, wmOperator *op, const wmEvent *event)
559 {
560   PropertyRNA *prop;
561
562   op->customdata = WM_gesture_new(C, event, WM_GESTURE_LASSO);
563
564   /* add modal handler */
565   WM_event_add_modal_handler(C, op);
566
567   wm_gesture_tag_redraw(C);
568
569   if ((prop = RNA_struct_find_property(op->ptr, "cursor"))) {
570     WM_cursor_modal_set(CTX_wm_window(C), RNA_property_int_get(op->ptr, prop));
571   }
572
573   return OPERATOR_RUNNING_MODAL;
574 }
575
576 int WM_gesture_lines_invoke(bContext *C, wmOperator *op, const wmEvent *event)
577 {
578   PropertyRNA *prop;
579
580   op->customdata = WM_gesture_new(C, event, WM_GESTURE_LINES);
581
582   /* add modal handler */
583   WM_event_add_modal_handler(C, op);
584
585   wm_gesture_tag_redraw(C);
586
587   if ((prop = RNA_struct_find_property(op->ptr, "cursor"))) {
588     WM_cursor_modal_set(CTX_wm_window(C), RNA_property_int_get(op->ptr, prop));
589   }
590
591   return OPERATOR_RUNNING_MODAL;
592 }
593
594 static void gesture_lasso_apply(bContext *C, wmOperator *op)
595 {
596   wmGesture *gesture = op->customdata;
597   PointerRNA itemptr;
598   float loc[2];
599   int i;
600   const short *lasso = gesture->customdata;
601
602   /* operator storage as path. */
603
604   RNA_collection_clear(op->ptr, "path");
605   for (i = 0; i < gesture->points; i++, lasso += 2) {
606     loc[0] = lasso[0];
607     loc[1] = lasso[1];
608     RNA_collection_add(op->ptr, "path", &itemptr);
609     RNA_float_set_array(&itemptr, "loc", loc);
610   }
611
612   gesture_modal_end(C, op);
613
614   if (op->type->exec) {
615     int retval = op->type->exec(C, op);
616     OPERATOR_RETVAL_CHECK(retval);
617   }
618 }
619
620 int WM_gesture_lasso_modal(bContext *C, wmOperator *op, const wmEvent *event)
621 {
622   wmGesture *gesture = op->customdata;
623
624   switch (event->type) {
625     case MOUSEMOVE:
626     case INBETWEEN_MOUSEMOVE:
627
628       wm_gesture_tag_redraw(C);
629
630       if (gesture->points == gesture->points_alloc) {
631         gesture->points_alloc *= 2;
632         gesture->customdata = MEM_reallocN(gesture->customdata,
633                                            sizeof(short[2]) * gesture->points_alloc);
634       }
635
636       {
637         int x, y;
638         short *lasso = gesture->customdata;
639
640         lasso += (2 * gesture->points - 2);
641         x = (event->x - gesture->winrct.xmin - lasso[0]);
642         y = (event->y - gesture->winrct.ymin - lasso[1]);
643
644         /* make a simple distance check to get a smoother lasso
645          * add only when at least 2 pixels between this and previous location */
646         if ((x * x + y * y) > 4) {
647           lasso += 2;
648           lasso[0] = event->x - gesture->winrct.xmin;
649           lasso[1] = event->y - gesture->winrct.ymin;
650           gesture->points++;
651         }
652       }
653       break;
654
655     case LEFTMOUSE:
656     case MIDDLEMOUSE:
657     case RIGHTMOUSE:
658       if (event->val == KM_RELEASE) { /* key release */
659         gesture_lasso_apply(C, op);
660         return OPERATOR_FINISHED;
661       }
662       break;
663     case ESCKEY:
664       gesture_modal_end(C, op);
665       return OPERATOR_CANCELLED;
666   }
667
668   gesture->is_active_prev = gesture->is_active;
669   return OPERATOR_RUNNING_MODAL;
670 }
671
672 int WM_gesture_lines_modal(bContext *C, wmOperator *op, const wmEvent *event)
673 {
674   return WM_gesture_lasso_modal(C, op, event);
675 }
676
677 void WM_gesture_lasso_cancel(bContext *C, wmOperator *op)
678 {
679   gesture_modal_end(C, op);
680 }
681
682 void WM_gesture_lines_cancel(bContext *C, wmOperator *op)
683 {
684   gesture_modal_end(C, op);
685 }
686
687 /**
688  * helper function, we may want to add options for conversion to view space
689  *
690  * caller must free.
691  */
692 const int (*WM_gesture_lasso_path_to_array(bContext *UNUSED(C),
693                                            wmOperator *op,
694                                            int *mcords_tot))[2]
695 {
696   PropertyRNA *prop = RNA_struct_find_property(op->ptr, "path");
697   int(*mcords)[2] = NULL;
698   BLI_assert(prop != NULL);
699
700   if (prop) {
701     const int len = RNA_property_collection_length(op->ptr, prop);
702
703     if (len) {
704       int i = 0;
705       mcords = MEM_mallocN(sizeof(int) * 2 * len, __func__);
706
707       RNA_PROP_BEGIN (op->ptr, itemptr, prop) {
708         float loc[2];
709
710         RNA_float_get_array(&itemptr, "loc", loc);
711         mcords[i][0] = (int)loc[0];
712         mcords[i][1] = (int)loc[1];
713         i++;
714       }
715       RNA_PROP_END;
716     }
717     *mcords_tot = len;
718   }
719   else {
720     *mcords_tot = 0;
721   }
722
723   /* cast for 'const' */
724   return (const int(*)[2])mcords;
725 }
726
727 #if 0
728 /* template to copy from */
729
730 static int gesture_lasso_exec(bContext *C, wmOperator *op)
731 {
732   RNA_BEGIN (op->ptr, itemptr, "path") {
733     float loc[2];
734
735     RNA_float_get_array(&itemptr, "loc", loc);
736     printf("Location: %f %f\n", loc[0], loc[1]);
737   }
738   RNA_END;
739
740   return OPERATOR_FINISHED;
741 }
742
743 void WM_OT_lasso_gesture(wmOperatorType *ot)
744 {
745   PropertyRNA *prop;
746
747   ot->name = "Lasso Gesture";
748   ot->idname = "WM_OT_lasso_gesture";
749   ot->description = "Select objects within the lasso as you move the pointer";
750
751   ot->invoke = WM_gesture_lasso_invoke;
752   ot->modal = WM_gesture_lasso_modal;
753   ot->exec = gesture_lasso_exec;
754
755   ot->poll = WM_operator_winactive;
756
757   prop = RNA_def_property(ot->srna, "path", PROP_COLLECTION, PROP_NONE);
758   RNA_def_property_struct_runtime(prop, &RNA_OperatorMousePath);
759 }
760 #endif
761
762 /** \} */
763
764 /* -------------------------------------------------------------------- */
765 /** \name Straight Line Gesture
766  * \{ */
767
768 static bool gesture_straightline_apply(bContext *C, wmOperator *op)
769 {
770   wmGesture *gesture = op->customdata;
771   rcti *rect = gesture->customdata;
772
773   if (rect->xmin == rect->xmax && rect->ymin == rect->ymax) {
774     return 0;
775   }
776
777   /* operator arguments and storage. */
778   RNA_int_set(op->ptr, "xstart", rect->xmin);
779   RNA_int_set(op->ptr, "ystart", rect->ymin);
780   RNA_int_set(op->ptr, "xend", rect->xmax);
781   RNA_int_set(op->ptr, "yend", rect->ymax);
782
783   if (op->type->exec) {
784     int retval = op->type->exec(C, op);
785     OPERATOR_RETVAL_CHECK(retval);
786   }
787
788   return 1;
789 }
790
791 int WM_gesture_straightline_invoke(bContext *C, wmOperator *op, const wmEvent *event)
792 {
793   PropertyRNA *prop;
794
795   op->customdata = WM_gesture_new(C, event, WM_GESTURE_STRAIGHTLINE);
796
797   if (ISTWEAK(event->type)) {
798     wmGesture *gesture = op->customdata;
799     gesture->is_active = true;
800   }
801
802   /* add modal handler */
803   WM_event_add_modal_handler(C, op);
804
805   wm_gesture_tag_redraw(C);
806
807   if ((prop = RNA_struct_find_property(op->ptr, "cursor"))) {
808     WM_cursor_modal_set(CTX_wm_window(C), RNA_property_int_get(op->ptr, prop));
809   }
810
811   return OPERATOR_RUNNING_MODAL;
812 }
813
814 int WM_gesture_straightline_modal(bContext *C, wmOperator *op, const wmEvent *event)
815 {
816   wmGesture *gesture = op->customdata;
817   rcti *rect = gesture->customdata;
818
819   if (event->type == MOUSEMOVE) {
820     if (gesture->is_active == false) {
821       rect->xmin = rect->xmax = event->x - gesture->winrct.xmin;
822       rect->ymin = rect->ymax = event->y - gesture->winrct.ymin;
823     }
824     else {
825       rect->xmax = event->x - gesture->winrct.xmin;
826       rect->ymax = event->y - gesture->winrct.ymin;
827       gesture_straightline_apply(C, op);
828     }
829
830     wm_gesture_tag_redraw(C);
831   }
832   else if (event->type == EVT_MODAL_MAP) {
833     switch (event->val) {
834       case GESTURE_MODAL_BEGIN:
835         if (gesture->is_active == false) {
836           gesture->is_active = true;
837           wm_gesture_tag_redraw(C);
838         }
839         break;
840       case GESTURE_MODAL_SELECT:
841         if (gesture_straightline_apply(C, op)) {
842           gesture_modal_end(C, op);
843           return OPERATOR_FINISHED;
844         }
845         gesture_modal_end(C, op);
846         return OPERATOR_CANCELLED;
847
848       case GESTURE_MODAL_CANCEL:
849         gesture_modal_end(C, op);
850         return OPERATOR_CANCELLED;
851     }
852   }
853
854   gesture->is_active_prev = gesture->is_active;
855   return OPERATOR_RUNNING_MODAL;
856 }
857
858 void WM_gesture_straightline_cancel(bContext *C, wmOperator *op)
859 {
860   gesture_modal_end(C, op);
861 }
862
863 #if 0
864 /* template to copy from */
865 void WM_OT_straightline_gesture(wmOperatorType *ot)
866 {
867   PropertyRNA *prop;
868
869   ot->name = "Straight Line Gesture";
870   ot->idname = "WM_OT_straightline_gesture";
871   ot->description = "Draw a straight line as you move the pointer";
872
873   ot->invoke = WM_gesture_straightline_invoke;
874   ot->modal = WM_gesture_straightline_modal;
875   ot->exec = gesture_straightline_exec;
876
877   ot->poll = WM_operator_winactive;
878
879   WM_operator_properties_gesture_straightline(ot, 0);
880 }
881 #endif
882
883 /** \} */