change clip utility function arguments to take space data and region rather then...
[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                 return sc->view == SC_VIEW_GRAPH;
70         }
71
72         return FALSE;
73 }
74
75 typedef struct {
76         int action;
77 } SelectUserData;
78
79 static void toggle_selection_cb(void *userdata, MovieTrackingMarker *marker)
80 {
81         SelectUserData *data = (SelectUserData *)userdata;
82
83         switch (data->action) {
84                 case SEL_SELECT:
85                         marker->flag |= MARKER_GRAPH_SEL;
86                         break;
87                 case SEL_DESELECT:
88                         marker->flag &= ~MARKER_GRAPH_SEL;
89                         break;
90                 case SEL_INVERT:
91                         marker->flag ^= MARKER_GRAPH_SEL;
92                         break;
93         }
94 }
95
96 /******************** mouse select operator ********************/
97
98 typedef struct {
99         int coord,          /* coordinate index of found entuty (0 = X-axis, 1 = Y-axis) */
100             has_prev;       /* if there's valid coordinate of previous point of curve segment */
101
102         float min_dist,     /* minimal distance between mouse and currently found entuty */
103               mouse_co[2],  /* mouse coordinate */
104               prev_co[2],   /* coordinate of previeous point of segment */
105               min_co[2];    /* coordinate of entity with minimal distance */
106
107         MovieTrackingTrack *track;      /* nearest found track */
108         MovieTrackingMarker *marker;    /* nearest found marker */
109 } MouseSelectUserData;
110
111 static void find_nearest_tracking_segment_cb(void *userdata, MovieTrackingTrack *track,
112                                              MovieTrackingMarker *UNUSED(marker),
113                                              int coord, int scene_framenr, float val)
114 {
115         MouseSelectUserData *data = userdata;
116         float co[2] = {scene_framenr, val};
117
118         if (data->has_prev) {
119                 float d = dist_to_line_segment_v2(data->mouse_co, data->prev_co, co);
120
121                 if (data->track == NULL || d < data->min_dist) {
122                         data->track = track;
123                         data->min_dist = d;
124                         data->coord = coord;
125                         copy_v2_v2(data->min_co, co);
126                 }
127         }
128
129         data->has_prev = TRUE;
130         copy_v2_v2(data->prev_co, co);
131 }
132
133 void find_nearest_tracking_segment_end_cb(void *userdata)
134 {
135         MouseSelectUserData *data = userdata;
136
137         data->has_prev = FALSE;
138 }
139
140 static void find_nearest_tracking_knot_cb(void *userdata, MovieTrackingTrack *track,
141                                           MovieTrackingMarker *marker, int coord, int scene_framenr, float val)
142 {
143         MouseSelectUserData *data = userdata;
144         float dx = scene_framenr - data->mouse_co[0], dy = val - data->mouse_co[1];
145         float d = dx * dx + dy * dy;
146
147         if (data->marker == NULL || d < data->min_dist) {
148                 float co[2] = {scene_framenr, val};
149
150                 data->track = track;
151                 data->marker = marker;
152                 data->min_dist = d;
153                 data->coord = coord;
154                 copy_v2_v2(data->min_co, co);
155         }
156
157 }
158
159 static void mouse_select_init_data(MouseSelectUserData *userdata, float *co)
160 {
161         memset(userdata, 0, sizeof(MouseSelectUserData));
162         userdata->min_dist = FLT_MAX;
163         copy_v2_v2(userdata->mouse_co, co);
164 }
165
166 static int mouse_select_knot(bContext *C, float co[2], int extend)
167 {
168         SpaceClip *sc = CTX_wm_space_clip(C);
169         MovieClip *clip = ED_space_clip_get_clip(sc);
170         ARegion *ar = CTX_wm_region(C);
171         View2D *v2d = &ar->v2d;
172         MovieTracking *tracking = &clip->tracking;
173         MovieTrackingTrack *act_track = BKE_tracking_track_get_active(tracking);
174         static const int delta = 6;
175
176         if (act_track) {
177                 MouseSelectUserData userdata;
178
179                 mouse_select_init_data(&userdata, co);
180                 clip_graph_tracking_values_iterate_track(sc, act_track, &userdata,
181                                                          find_nearest_tracking_knot_cb, NULL, NULL);
182
183                 if (userdata.marker) {
184                         int x1, y1, x2, y2;
185
186                         UI_view2d_view_to_region(v2d, co[0], co[1], &x1, &y1);
187                         UI_view2d_view_to_region(v2d, userdata.min_co[0], userdata.min_co[1], &x2, &y2);
188
189                         if (abs(x2 - x1) <= delta && abs(y2 - y1) <= delta) {
190                                 if (!extend) {
191                                         SelectUserData selectdata = {SEL_DESELECT};
192
193                                         clip_graph_tracking_iterate(sc, sc->flag & SC_SHOW_GRAPH_SEL_ONLY,
194                                                                     sc->flag & SC_SHOW_GRAPH_HIDDEN, &selectdata,
195                                                                     toggle_selection_cb);
196                                 }
197
198                                 if (userdata.coord == 0)
199                                         userdata.marker->flag |= MARKER_GRAPH_SEL_X;
200                                 else
201                                         userdata.marker->flag |= MARKER_GRAPH_SEL_Y;
202
203                                 return TRUE;
204                         }
205                 }
206         }
207
208         return FALSE;
209 }
210
211 static int mouse_select_curve(bContext *C, float co[2], int extend)
212 {
213         SpaceClip *sc = CTX_wm_space_clip(C);
214         MovieClip *clip = ED_space_clip_get_clip(sc);
215         MovieTracking *tracking = &clip->tracking;
216         MovieTrackingTrack *act_track = BKE_tracking_track_get_active(tracking);
217         MouseSelectUserData userdata;
218
219         mouse_select_init_data(&userdata, co);
220         clip_graph_tracking_values_iterate(sc, sc->flag & SC_SHOW_GRAPH_SEL_ONLY, sc->flag & SC_SHOW_GRAPH_HIDDEN,
221                                            &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                         SelectUserData selectdata = {SEL_DESELECT};
233                         MovieTrackingObject *object = BKE_tracking_object_get_active(tracking);
234                         ListBase *tracksbase = BKE_tracking_object_get_tracks(tracking, object);
235
236                         tracking->act_track = userdata.track;
237                         BKE_tracking_track_select(tracksbase, userdata.track, TRACK_AREA_ALL, TRUE);
238
239                         /* deselect all knots on newly selected curve */
240                         clip_graph_tracking_iterate(sc, sc->flag & SC_SHOW_GRAPH_SEL_ONLY,
241                                                     sc->flag & SC_SHOW_GRAPH_HIDDEN, &selectdata,
242                                                     toggle_selection_cb);
243                 }
244
245                 return TRUE;
246         }
247
248         return FALSE;
249 }
250
251 static int mouse_select(bContext *C, float co[2], int extend)
252 {
253         int sel = FALSE;
254
255         /* first try to select knot on selected curves */
256         sel = mouse_select_knot(C, co, extend);
257
258         if (!sel) {
259                 /* if there's no close enough knot to mouse osition, select nearest curve */
260                 sel = mouse_select_curve(C, co, extend);
261         }
262
263         if (sel)
264                 WM_event_add_notifier(C, NC_GEOM | ND_SELECT, NULL);
265
266         return OPERATOR_FINISHED;
267 }
268
269 static int select_exec(bContext *C, wmOperator *op)
270 {
271         float co[2];
272         int extend = RNA_boolean_get(op->ptr, "extend");
273
274         RNA_float_get_array(op->ptr, "location", co);
275
276         return mouse_select(C, co, extend);
277 }
278
279 static int select_invoke(bContext *C, wmOperator *op, wmEvent *event)
280 {
281         ARegion *ar = CTX_wm_region(C);
282         float co[2];
283
284         UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &co[0], &co[1]);
285         RNA_float_set_array(op->ptr, "location", co);
286
287         return select_exec(C, op);
288 }
289
290 void CLIP_OT_graph_select(wmOperatorType *ot)
291 {
292         /* identifiers */
293         ot->name = "Select";
294         ot->description = "Select graph curves";
295         ot->idname = "CLIP_OT_graph_select";
296
297         /* api callbacks */
298         ot->exec = select_exec;
299         ot->invoke = select_invoke;
300         ot->poll = ED_space_clip_graph_poll;
301
302         /* flags */
303         ot->flag = OPTYPE_UNDO;
304
305         /* properties */
306         RNA_def_float_vector(ot->srna, "location", 2, NULL, -FLT_MAX, FLT_MAX,
307                              "Location", "Mouse location to select nearest entity", -100.0f, 100.0f);
308         RNA_def_boolean(ot->srna, "extend", 0,
309                         "Extend", "Extend selection rather than clearing the existing selection");
310 }
311
312 /********************** border select operator *********************/
313
314 typedef struct BorderSelectuserData {
315         rctf rect;
316         int change, mode, extend;
317 } BorderSelectuserData;
318
319 static void border_select_cb(void *userdata, MovieTrackingTrack *UNUSED(track),
320                              MovieTrackingMarker *marker, int coord, int scene_framenr, float val)
321 {
322         BorderSelectuserData *data = (BorderSelectuserData *) userdata;
323
324         if (BLI_in_rctf(&data->rect, scene_framenr, val)) {
325                 int flag = 0;
326
327                 if (coord == 0)
328                         flag = MARKER_GRAPH_SEL_X;
329                 else
330                         flag = MARKER_GRAPH_SEL_Y;
331
332                 if (data->mode == GESTURE_MODAL_SELECT)
333                         marker->flag |= flag;
334                 else
335                         marker->flag &= ~flag;
336
337                 data->change = TRUE;
338         }
339         else if (!data->extend) {
340                 marker->flag &= ~MARKER_GRAPH_SEL;
341         }
342 }
343
344 static int border_select_graph_exec(bContext *C, wmOperator *op)
345 {
346         SpaceClip *sc = CTX_wm_space_clip(C);
347         ARegion *ar = CTX_wm_region(C);
348
349         MovieClip *clip = ED_space_clip_get_clip(sc);
350         MovieTracking *tracking = &clip->tracking;
351         MovieTrackingTrack *act_track = BKE_tracking_track_get_active(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_get_clip(sc);
405         MovieTracking *tracking = &clip->tracking;
406         MovieTrackingTrack *act_track = BKE_tracking_track_get_active(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_get_clip(sc);
471         MovieTracking *tracking = &clip->tracking;
472         ListBase *tracksbase = BKE_tracking_get_active_tracks(tracking);
473         MovieTrackingTrack *act_track = BKE_tracking_track_get_active(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_get_clip(sc);
503         MovieTracking *tracking = &clip->tracking;
504         ListBase *tracksbase = BKE_tracking_get_active_tracks(tracking);
505         MovieTrackingTrack *act_track = BKE_tracking_track_get_active(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), int UNUSED(scene_framenr), 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, sc->flag & SC_SHOW_GRAPH_SEL_ONLY,
569                                            sc->flag & SC_SHOW_GRAPH_HIDDEN, &userdata,
570                                            view_all_cb, NULL, NULL);
571
572         /* set extents of view to start/end frames */
573         v2d->cur.xmin = (float) SFRA;
574         v2d->cur.xmax = (float) EFRA;
575
576         if (userdata.min < userdata.max) {
577                 v2d->cur.ymin = userdata.min;
578                 v2d->cur.ymax = userdata.max;
579         }
580         else {
581                 v2d->cur.ymin = -10;
582                 v2d->cur.ymax = 10;
583         }
584
585         /* we need an extra "buffer" factor on either side so that the endpoints are visible */
586         extra = 0.01f * (v2d->cur.xmax - v2d->cur.xmin);
587         v2d->cur.xmin -= extra;
588         v2d->cur.xmax += extra;
589
590         extra = 0.01f * (v2d->cur.ymax - v2d->cur.ymin);
591         v2d->cur.ymin -= extra;
592         v2d->cur.ymax += extra;
593
594         ED_region_tag_redraw(ar);
595
596         return OPERATOR_FINISHED;
597 }
598
599 void CLIP_OT_graph_view_all(wmOperatorType *ot)
600 {
601         /* identifiers */
602         ot->name = "View All";
603         ot->description = "View all curves in editor";
604         ot->idname = "CLIP_OT_graph_view_all";
605
606         /* api callbacks */
607         ot->exec = view_all_exec;
608         ot->poll = ED_space_clip_graph_poll;
609 }
610
611 /******************** jump to current frame operator ********************/
612
613 void ED_clip_graph_center_current_frame(Scene *scene, ARegion *ar)
614 {
615         View2D *v2d = &ar->v2d;
616         float extra = (v2d->cur.xmax - v2d->cur.xmin) / 2.0f;
617
618         /* set extents of view to start/end frames */
619         v2d->cur.xmin = (float)CFRA - extra;
620         v2d->cur.xmax = (float)CFRA + extra;
621 }
622
623 static int center_current_frame_exec(bContext *C, wmOperator *UNUSED(op))
624 {
625         Scene *scene = CTX_data_scene(C);
626         ARegion *ar = CTX_wm_region(C);
627
628         ED_clip_graph_center_current_frame(scene, ar);
629
630         ED_region_tag_redraw(ar);
631
632         return OPERATOR_FINISHED;
633 }
634
635 void CLIP_OT_graph_center_current_frame(wmOperatorType *ot)
636 {
637         /* identifiers */
638         ot->name = "Center Current Frame";
639         ot->description = "Scroll view so current frame would be centered";
640         ot->idname = "CLIP_OT_graph_center_current_frame";
641
642         /* api callbacks */
643         ot->exec = center_current_frame_exec;
644         ot->poll = ED_space_clip_graph_poll;
645 }
646
647 /********************** disable markers operator *********************/
648
649 static int graph_disable_markers_exec(bContext *C, wmOperator *op)
650 {
651         SpaceClip *sc = CTX_wm_space_clip(C);
652         MovieClip *clip = ED_space_clip_get_clip(sc);
653         MovieTracking *tracking = &clip->tracking;
654         MovieTrackingTrack *act_track = BKE_tracking_track_get_active(tracking);
655         MovieTrackingMarker *marker;
656         int action = RNA_enum_get(op->ptr, "action");
657         int a;
658
659         if (!act_track || (act_track->flag & TRACK_LOCKED))
660                 return OPERATOR_CANCELLED;
661
662         for (a = 0; a < act_track->markersnr; a++) {
663                 marker = &act_track->markers[a];
664
665                 if (marker->flag & MARKER_GRAPH_SEL) {
666                         if (action == 0)
667                                 marker->flag |= MARKER_DISABLED;
668                         else if (action == 1)
669                                 marker->flag &= ~MARKER_DISABLED;
670                         else
671                                 marker->flag ^= MARKER_DISABLED;
672                 }
673         }
674
675         DAG_id_tag_update(&clip->id, 0);
676
677         WM_event_add_notifier(C, NC_MOVIECLIP | NA_EVALUATED, clip);
678
679         return OPERATOR_FINISHED;
680 }
681
682 void CLIP_OT_graph_disable_markers(wmOperatorType *ot)
683 {
684         static EnumPropertyItem actions_items[] = {
685                 {0, "DISABLE", 0, "Disable", "Disable selected markers"},
686                 {1, "ENABLE", 0, "Enable", "Enable selected markers"},
687                 {2, "TOGGLE", 0, "Toggle", "Toggle disabled flag for selected markers"},
688                 {0, NULL, 0, NULL, NULL}
689         };
690
691         /* identifiers */
692         ot->name = "Disable Markers";
693         ot->description = "Disable/enable selected markers";
694         ot->idname = "CLIP_OT_graph_disable_markers";
695
696         /* api callbacks */
697         ot->exec = graph_disable_markers_exec;
698         ot->poll = ED_space_clip_graph_poll;
699
700         /* flags */
701         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
702
703         /* properties */
704         RNA_def_enum(ot->srna, "action", actions_items, 0, "Action", "Disable action to execute");
705 }