Merging r46062 through r46073 from trunk into soc-2011-tomato
[blender.git] / source / blender / editors / space_clip / clip_graph_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) 2011 Blender Foundation.
19  * All rights reserved.
20  *
21  *
22  * Contributor(s): Blender Foundation,
23  *                 Sergey Sharybin
24  *
25  * ***** END GPL LICENSE BLOCK *****
26  */
27
28 /** \file blender/editors/space_clip/clip_graph_ops.c
29  *  \ingroup spclip
30  */
31
32 #include "DNA_object_types.h"   /* SELECT */
33 #include "DNA_scene_types.h"
34
35 #include "MEM_guardedalloc.h"
36
37 #include "BLI_utildefines.h"
38 #include "BLI_math.h"
39 #include "BLI_listbase.h"
40 #include "BLI_rect.h"
41
42 #include "BKE_context.h"
43 #include "BKE_movieclip.h"
44 #include "BKE_tracking.h"
45 #include "BKE_depsgraph.h"
46
47 #include "WM_api.h"
48 #include "WM_types.h"
49
50 #include "ED_screen.h"
51 #include "ED_clip.h"
52
53 #include "UI_interface.h"
54
55 #include "RNA_access.h"
56 #include "RNA_define.h"
57
58 #include "UI_view2d.h"
59
60 #include "clip_intern.h"        // own include
61
62 /******************** common graph-editing utilities ********************/
63
64 static int ED_space_clip_graph_poll(bContext *C)
65 {
66         if (ED_space_clip_tracking_poll(C)) {
67                 SpaceClip *sc = CTX_wm_space_clip(C);
68
69                 if (sc->view == SC_VIEW_GRAPH) {
70                         ARegion *ar = CTX_wm_region(C);
71
72                         return ar->regiontype == RGN_TYPE_PREVIEW;
73                 }
74         }
75
76         return FALSE;
77 }
78
79 typedef struct {
80         int action;
81 } SelectUserData;
82
83 static void toggle_selection_cb(void *userdata, MovieTrackingMarker *marker)
84 {
85         SelectUserData *data = (SelectUserData *)userdata;
86
87         switch (data->action) {
88                 case SEL_SELECT:
89                         marker->flag |= MARKER_GRAPH_SEL;
90                         break;
91                 case SEL_DESELECT:
92                         marker->flag &= ~MARKER_GRAPH_SEL;
93                         break;
94                 case SEL_INVERT:
95                         marker->flag ^= MARKER_GRAPH_SEL;
96                         break;
97         }
98 }
99
100 /******************** mouse select operator ********************/
101
102 typedef struct {
103         int coord,              /* coordinate index of found entuty (0 = X-axis, 1 = Y-axis) */
104             has_prev;           /* if there's valid coordinate of previous point of curve segment */
105
106         float min_dist,         /* minimal distance between mouse and currently found entuty */
107               mouse_co[2],      /* mouse coordinate */
108               prev_co[2],       /* coordinate of previeous point of segment */
109               min_co[2];        /* coordinate of entity with minimal distance */
110
111         MovieTrackingTrack *track;      /* nearest found track */
112         MovieTrackingMarker *marker;    /* nearest found marker */
113 } MouseSelectUserData;
114
115 static void find_nearest_tracking_segment_cb(void *userdata, MovieTrackingTrack *track,
116                                              MovieTrackingMarker *marker, int coord, float val)
117 {
118         MouseSelectUserData *data = userdata;
119         float co[2] = {marker->framenr, val};
120
121         if (data->has_prev) {
122                 float d = dist_to_line_segment_v2(data->mouse_co, data->prev_co, co);
123
124                 if (data->track == NULL || d < data->min_dist) {
125                         data->track = track;
126                         data->min_dist = d;
127                         data->coord = coord;
128                         copy_v2_v2(data->min_co, co);
129                 }
130         }
131
132         data->has_prev = TRUE;
133         copy_v2_v2(data->prev_co, co);
134 }
135
136 void find_nearest_tracking_segment_end_cb(void *userdata)
137 {
138         MouseSelectUserData *data = userdata;
139
140         data->has_prev = FALSE;
141 }
142
143 static void find_nearest_tracking_knot_cb(void *userdata, MovieTrackingTrack *track,
144                                           MovieTrackingMarker *marker, int coord, float val)
145 {
146         MouseSelectUserData *data = userdata;
147         float dx = marker->framenr - data->mouse_co[0], dy = val - data->mouse_co[1];
148         float d = dx * dx + dy * dy;
149
150         if (data->marker == NULL || d < data->min_dist) {
151                 float co[2]= {marker->framenr, val};
152
153                 data->track = track;
154                 data->marker = marker;
155                 data->min_dist = d;
156                 data->coord = coord;
157                 copy_v2_v2(data->min_co, co);
158         }
159
160 }
161
162 static void mouse_select_init_data(MouseSelectUserData *userdata, float *co)
163 {
164         memset(userdata, 0, sizeof(MouseSelectUserData));
165         userdata->min_dist = FLT_MAX;
166         copy_v2_v2(userdata->mouse_co, co);
167 }
168
169 static int mouse_select_knot(bContext *C, float co[2], int extend)
170 {
171         SpaceClip *sc = CTX_wm_space_clip(C);
172         MovieClip *clip= ED_space_clip(sc);
173         ARegion *ar = CTX_wm_region(C);
174         View2D *v2d = &ar->v2d;
175         MovieTracking *tracking = &clip->tracking;
176         MovieTrackingTrack *act_track = BKE_tracking_active_track(tracking);
177         static const int delta = 6;
178
179         if (act_track) {
180                 MouseSelectUserData userdata;
181
182                 mouse_select_init_data(&userdata, co);
183                 clip_graph_tracking_values_iterate_track(sc, act_track, &userdata,
184                                                          find_nearest_tracking_knot_cb, NULL, NULL);
185
186                 if (userdata.marker) {
187                         int x1, y1, x2, y2;
188
189                         UI_view2d_view_to_region(v2d, co[0], co[1], &x1, &y1);
190                         UI_view2d_view_to_region(v2d, userdata.min_co[0], userdata.min_co[1], &x2, &y2);
191
192                         if (abs(x2 - x1) <= delta && abs(y2 - y1) <= delta) {
193                                 if (!extend) {
194                                         SelectUserData selectdata = {SEL_DESELECT};
195
196                                         clip_graph_tracking_iterate(sc, &selectdata, toggle_selection_cb);
197                                 }
198
199                                 if (userdata.coord == 0)
200                                         userdata.marker->flag |= MARKER_GRAPH_SEL_X;
201                                 else
202                                         userdata.marker->flag |= MARKER_GRAPH_SEL_Y;
203
204                                 return TRUE;
205                         }
206                 }
207         }
208
209         return FALSE;
210 }
211
212 static int mouse_select_curve(bContext *C, float co[2], int extend)
213 {
214         SpaceClip *sc = CTX_wm_space_clip(C);
215         MovieClip *clip = ED_space_clip(sc);
216         MovieTracking *tracking = &clip->tracking;
217         MovieTrackingTrack *act_track = BKE_tracking_active_track(tracking);
218         MouseSelectUserData userdata;
219
220         mouse_select_init_data(&userdata, co);
221         clip_graph_tracking_values_iterate(sc, &userdata, find_nearest_tracking_segment_cb,
222                                            NULL, find_nearest_tracking_segment_end_cb);
223
224         if (userdata.track) {
225                 if (extend) {
226                         if (act_track == userdata.track) {
227                                 /* currently only single curve can be selected (selected curve represents active track) */
228                                 act_track = NULL;
229                         }
230                 }
231                 else if (act_track != userdata.track) {
232                         MovieTrackingMarker *marker;
233                         SelectUserData selectdata = {SEL_DESELECT};
234
235                         tracking->act_track = userdata.track;
236
237                         /* make active track be centered to screen */
238                         /* XXX: doesn't work in other opened spaces */
239                         marker = BKE_tracking_get_marker(userdata.track, sc->user.framenr);
240                         clip_view_center_to_point(sc, marker->pos[0], marker->pos[1]);
241
242                         /* deselect all knots on newly selected curve */
243                         clip_graph_tracking_iterate(sc, &selectdata, toggle_selection_cb);
244                 }
245
246                 return TRUE;
247         }
248
249         return FALSE;
250 }
251
252 static int mouse_select(bContext *C, float co[2], int extend)
253 {
254         int sel = FALSE;
255
256         /* first try to select knot on selected curves */
257         sel = mouse_select_knot(C, co, extend);
258
259         if (!sel) {
260                 /* if there's no close enough knot to mouse osition, select nearest curve */
261                 sel = mouse_select_curve(C, co, extend);
262         }
263
264         if (sel)
265                 WM_event_add_notifier(C, NC_GEOM|ND_SELECT, NULL);
266
267         return OPERATOR_FINISHED;
268 }
269
270 static int select_exec(bContext *C, wmOperator *op)
271 {
272         float co[2];
273         int  extend = RNA_boolean_get(op->ptr, "extend");
274
275         RNA_float_get_array(op->ptr, "location", co);
276
277         return mouse_select(C, co, extend);
278 }
279
280 static int select_invoke(bContext *C, wmOperator *op, wmEvent *event)
281 {
282         ARegion *ar = CTX_wm_region(C);
283         float co[2];
284
285         UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &co[0], &co[1]);
286         RNA_float_set_array(op->ptr, "location", co);
287
288         return select_exec(C, op);
289 }
290
291 void CLIP_OT_graph_select(wmOperatorType *ot)
292 {
293         /* identifiers */
294         ot->name = "Select";
295         ot->description = "Select graph curves";
296         ot->idname = "CLIP_OT_graph_select";
297
298         /* api callbacks */
299         ot->exec = select_exec;
300         ot->invoke = select_invoke;
301         ot->poll = ED_space_clip_graph_poll;
302
303         /* flags */
304         ot->flag = OPTYPE_UNDO;
305
306         /* properties */
307         RNA_def_float_vector(ot->srna, "location", 2, NULL, -FLT_MAX, FLT_MAX,
308                 "Location", "Mouse location to select nearest entity", -100.0f, 100.0f);
309         RNA_def_boolean(ot->srna, "extend", 0,
310                 "Extend", "Extend selection rather than clearing the existing selection");
311 }
312
313 /********************** border select operator *********************/
314
315 typedef struct BorderSelectuserData {
316         rctf rect;
317         int change, mode, extend;
318 } BorderSelectuserData;
319
320 static void border_select_cb(void *userdata, MovieTrackingTrack *UNUSED(track),
321                              MovieTrackingMarker *marker, int coord, float val)
322 {
323         BorderSelectuserData *data = (BorderSelectuserData *) userdata;
324
325         if (BLI_in_rctf(&data->rect, marker->framenr, val)) {
326                 int flag = 0;
327
328                 if (coord == 0)
329                         flag = MARKER_GRAPH_SEL_X;
330                 else
331                         flag = MARKER_GRAPH_SEL_Y;
332
333                 if (data->mode == GESTURE_MODAL_SELECT)
334                         marker->flag |= flag;
335                 else
336                         marker->flag &= ~flag;
337
338                 data->change = TRUE;
339         }
340         else if (!data->extend) {
341                 marker->flag&= ~MARKER_GRAPH_SEL;
342         }
343 }
344
345 static int border_select_graph_exec(bContext *C, wmOperator *op)
346 {
347         SpaceClip *sc = CTX_wm_space_clip(C);
348         ARegion *ar = CTX_wm_region(C);
349         MovieClip *clip = ED_space_clip(sc);
350         MovieTracking *tracking = &clip->tracking;
351         MovieTrackingTrack *act_track = BKE_tracking_active_track(tracking);
352         BorderSelectuserData userdata;
353         rcti rect;
354
355         /* get rectangle from operator */
356         rect.xmin = RNA_int_get(op->ptr, "xmin");
357         rect.ymin = RNA_int_get(op->ptr, "ymin");
358         rect.xmax = RNA_int_get(op->ptr, "xmax");
359         rect.ymax = RNA_int_get(op->ptr, "ymax");
360
361         UI_view2d_region_to_view(&ar->v2d, rect.xmin, rect.ymin, &userdata.rect.xmin, &userdata.rect.ymin);
362         UI_view2d_region_to_view(&ar->v2d, rect.xmax, rect.ymax, &userdata.rect.xmax, &userdata.rect.ymax);
363
364         userdata.change = FALSE;
365         userdata.mode = RNA_int_get(op->ptr, "gesture_mode");
366         userdata.extend = RNA_boolean_get(op->ptr, "extend");
367
368         clip_graph_tracking_values_iterate_track(sc, act_track, &userdata, border_select_cb, NULL, NULL);
369
370         if (userdata.change) {
371                 WM_event_add_notifier(C, NC_GEOM|ND_SELECT, NULL);
372
373                 return OPERATOR_FINISHED;
374         }
375
376         return OPERATOR_CANCELLED;
377 }
378
379 void CLIP_OT_graph_select_border(wmOperatorType *ot)
380 {
381         /* identifiers */
382         ot->name = "Border Select";
383         ot->description = "Select curve points using border selection";
384         ot->idname = "CLIP_OT_graph_select_border";
385
386         /* api callbacks */
387         ot->invoke = WM_border_select_invoke;
388         ot->exec = border_select_graph_exec;
389         ot->modal = WM_border_select_modal;
390         ot->poll = ED_space_clip_graph_poll;
391
392         /* flags */
393         ot->flag = OPTYPE_UNDO;
394
395         /* properties */
396         WM_operator_properties_gesture_border(ot, TRUE);
397 }
398
399 /********************** select all operator *********************/
400
401 static int graph_select_all_markers_exec(bContext *C, wmOperator *op)
402 {
403         SpaceClip *sc = CTX_wm_space_clip(C);
404         MovieClip *clip = ED_space_clip(sc);
405         MovieTracking *tracking = &clip->tracking;
406         MovieTrackingTrack *act_track = BKE_tracking_active_track(tracking);
407         MovieTrackingMarker *marker;
408         int action = RNA_enum_get(op->ptr, "action");
409         int a;
410
411         if (!act_track)
412                 return OPERATOR_CANCELLED;
413
414         if (action == SEL_TOGGLE) {
415                 action = SEL_SELECT;
416
417                 for (a = 0; a < act_track->markersnr; a++) {
418                         marker = &act_track->markers[a];
419
420                         if (marker->flag & MARKER_GRAPH_SEL) {
421                                 action = SEL_DESELECT;
422                                 break;
423                         }
424                 }
425         }
426
427         for (a = 0; a < act_track->markersnr; a++) {
428                 marker = &act_track->markers[a];
429
430                 switch (action) {
431                         case SEL_SELECT:
432                                 marker->flag |= MARKER_GRAPH_SEL;
433                                 break;
434                         case SEL_DESELECT:
435                                 marker->flag &= ~MARKER_GRAPH_SEL;
436                                 break;
437                         case SEL_INVERT:
438                                 marker->flag ^= MARKER_GRAPH_SEL;
439                                 break;
440                 }
441         }
442
443         WM_event_add_notifier(C, NC_GEOM|ND_SELECT, NULL);
444
445         return OPERATOR_FINISHED;
446 }
447
448 void CLIP_OT_graph_select_all_markers(wmOperatorType *ot)
449 {
450         /* identifiers */
451         ot->name = "(De)select All Markers";
452         ot->description = "Change selection of all markers of active track";
453         ot->idname = "CLIP_OT_graph_select_all_markers";
454
455         /* api callbacks */
456         ot->exec = graph_select_all_markers_exec;
457         ot->poll = ED_space_clip_graph_poll;
458
459         /* flags */
460         ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
461
462         WM_operator_properties_select_all(ot);
463 }
464
465 /******************** delete curve operator ********************/
466
467 static int delete_curve_exec(bContext *C, wmOperator *UNUSED(op))
468 {
469         SpaceClip *sc = CTX_wm_space_clip(C);
470         MovieClip *clip = ED_space_clip(sc);
471         MovieTracking *tracking = &clip->tracking;
472         ListBase *tracksbase = BKE_tracking_get_tracks(tracking);
473         MovieTrackingTrack *act_track = BKE_tracking_active_track(tracking);
474
475         if (act_track)
476                 clip_delete_track(C, clip, tracksbase, act_track);
477
478         return OPERATOR_FINISHED;
479 }
480
481 void CLIP_OT_graph_delete_curve(wmOperatorType *ot)
482 {
483         /* identifiers */
484         ot->name = "Delete Curve";
485         ot->description = "Delete selected curves";
486         ot->idname = "CLIP_OT_graph_delete_curve";
487
488         /* api callbacks */
489         ot->invoke = WM_operator_confirm;
490         ot->exec = delete_curve_exec;
491         ot->poll = ED_space_clip_tracking_poll;
492
493         /* flags */
494         ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
495 }
496
497 /******************** delete knot operator ********************/
498
499 static int delete_knot_exec(bContext *C, wmOperator *UNUSED(op))
500 {
501         SpaceClip *sc = CTX_wm_space_clip(C);
502         MovieClip *clip= ED_space_clip(sc);
503         MovieTracking *tracking = &clip->tracking;
504         ListBase *tracksbase = BKE_tracking_get_tracks(tracking);
505         MovieTrackingTrack *act_track = BKE_tracking_active_track(tracking);
506
507         if (act_track) {
508                 int a = 0;
509
510                 while (a < act_track->markersnr) {
511                         MovieTrackingMarker *marker = &act_track->markers[a];
512
513                         if (marker->flag & MARKER_GRAPH_SEL)
514                                 clip_delete_marker(C, clip, tracksbase, act_track, marker);
515                         else
516                                 a++;
517                 }
518         }
519
520         return OPERATOR_FINISHED;
521 }
522
523 void CLIP_OT_graph_delete_knot(wmOperatorType *ot)
524 {
525         /* identifiers */
526         ot->name = "Delete Knot";
527         ot->description = "Delete curve knots";
528         ot->idname = "CLIP_OT_graph_delete_knot";
529
530         /* api callbacks */
531         ot->exec = delete_knot_exec;
532         ot->poll = ED_space_clip_graph_poll;
533
534         /* flags */
535         ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
536 }
537
538 /******************** view all operator ********************/
539
540 typedef struct {
541         float min, max;
542 } ViewAllUserData;
543
544 static void view_all_cb(void *userdata, MovieTrackingTrack *UNUSED(track), MovieTrackingMarker *UNUSED(marker),
545                         int UNUSED(coord), float val)
546 {
547         ViewAllUserData *data = (ViewAllUserData *) userdata;
548
549         if (val < data->min)
550                 data->min = val;
551
552         if (val > data->max)
553                 data->max = val;
554 }
555
556 static int view_all_exec(bContext *C, wmOperator *UNUSED(op))
557 {
558         Scene *scene = CTX_data_scene(C);
559         ARegion *ar = CTX_wm_region(C);
560         SpaceClip *sc = CTX_wm_space_clip(C);
561         View2D *v2d = &ar->v2d;
562         ViewAllUserData userdata;
563         float extra;
564
565         userdata.max = -FLT_MAX;
566         userdata.min = FLT_MAX;
567
568         clip_graph_tracking_values_iterate(sc, &userdata, view_all_cb, NULL, NULL);
569
570         /* set extents of view to start/end frames */
571         v2d->cur.xmin = (float) SFRA;
572         v2d->cur.xmax = (float) EFRA;
573
574         if (userdata.min < userdata.max) {
575                 v2d->cur.ymin = userdata.min;
576                 v2d->cur.ymax = userdata.max;
577         }
578         else {
579                 v2d->cur.ymin = -10;
580                 v2d->cur.ymax = 10;
581         }
582
583         /* we need an extra "buffer" factor on either side so that the endpoints are visible */
584         extra = 0.01f * (v2d->cur.xmax - v2d->cur.xmin);
585         v2d->cur.xmin -= extra;
586         v2d->cur.xmax += extra;
587
588         extra = 0.01f * (v2d->cur.ymax - v2d->cur.ymin);
589         v2d->cur.ymin -= extra;
590         v2d->cur.ymax += extra;
591
592         ED_region_tag_redraw(ar);
593
594         return OPERATOR_FINISHED;
595 }
596
597 void CLIP_OT_graph_view_all(wmOperatorType *ot)
598 {
599         /* identifiers */
600         ot->name = "View All";
601         ot->description = "View all curves in editor";
602         ot->idname = "CLIP_OT_graph_view_all";
603
604         /* api callbacks */
605         ot->exec = view_all_exec;
606         ot->poll = ED_space_clip_graph_poll;
607 }
608
609 /******************** jump to current frame operator ********************/
610
611 void ED_clip_graph_center_current_frame(Scene *scene, ARegion *ar)
612 {
613         View2D *v2d = &ar->v2d;
614         float extra = (v2d->cur.xmax - v2d->cur.xmin) / 2.0f;
615
616         /* set extents of view to start/end frames */
617         v2d->cur.xmin = (float)CFRA - extra;
618         v2d->cur.xmax = (float)CFRA + extra;
619 }
620
621 static int center_current_frame_exec(bContext *C, wmOperator *UNUSED(op))
622 {
623         Scene *scene = CTX_data_scene(C);
624         ARegion *ar = CTX_wm_region(C);
625
626         ED_clip_graph_center_current_frame(scene, ar);
627
628         ED_region_tag_redraw(ar);
629
630         return OPERATOR_FINISHED;
631 }
632
633 void CLIP_OT_graph_center_current_frame(wmOperatorType *ot)
634 {
635         /* identifiers */
636         ot->name = "Center Current Frame";
637         ot->description = "Scroll view so current frame would be centered";
638         ot->idname = "CLIP_OT_graph_center_current_frame";
639
640         /* api callbacks */
641         ot->exec = center_current_frame_exec;
642         ot->poll = ED_space_clip_graph_poll;
643 }
644
645 /********************** disable markers operator *********************/
646
647 static int graph_disable_markers_exec(bContext *C, wmOperator *op)
648 {
649         SpaceClip *sc = CTX_wm_space_clip(C);
650         MovieClip *clip = ED_space_clip(sc);
651         MovieTracking *tracking = &clip->tracking;
652         MovieTrackingTrack *act_track = BKE_tracking_active_track(tracking);
653         MovieTrackingMarker *marker;
654         int action = RNA_enum_get(op->ptr, "action");
655         int a;
656
657         if (!act_track || (act_track->flag & TRACK_LOCKED))
658                 return OPERATOR_CANCELLED;
659
660         for (a = 0; a < act_track->markersnr; a++) {
661                 marker = &act_track->markers[a];
662
663                 if (marker->flag & MARKER_GRAPH_SEL) {
664                         if (action==0)
665                                 marker->flag |= MARKER_DISABLED;
666                         else if (action==1)
667                                 marker->flag &= ~MARKER_DISABLED;
668                         else
669                                 marker->flag ^= MARKER_DISABLED;
670                 }
671         }
672
673         DAG_id_tag_update(&clip->id, 0);
674
675         WM_event_add_notifier(C, NC_MOVIECLIP|NA_EVALUATED, clip);
676
677         return OPERATOR_FINISHED;
678 }
679
680 void CLIP_OT_graph_disable_markers(wmOperatorType *ot)
681 {
682         static EnumPropertyItem actions_items[] = {
683                         {0, "DISABLE", 0, "Disable", "Disable selected markers"},
684                         {1, "ENABLE", 0, "Enable", "Enable selected markers"},
685                         {2, "TOGGLE", 0, "Toggle", "Toggle disabled flag for selected markers"},
686                         {0, NULL, 0, NULL, NULL}
687         };
688
689         /* identifiers */
690         ot->name = "Disable Markers";
691         ot->description = "Disable/enable selected markers";
692         ot->idname = "CLIP_OT_graph_disable_markers";
693
694         /* api callbacks */
695         ot->exec = graph_disable_markers_exec;
696         ot->poll = ED_space_clip_graph_poll;
697
698         /* flags */
699         ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
700
701         /* properties */
702         RNA_def_enum(ot->srna, "action", actions_items, 0, "Action", "Disable action to execute");
703 }