Internal refactoring of tracking module, should be no functional changes
[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(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(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         MovieClip *clip = ED_space_clip(sc);
349         MovieTracking *tracking = &clip->tracking;
350         MovieTrackingTrack *act_track = BKE_tracking_track_get_active(tracking);
351         BorderSelectuserData userdata;
352         rcti rect;
353
354         /* get rectangle from operator */
355         rect.xmin = RNA_int_get(op->ptr, "xmin");
356         rect.ymin = RNA_int_get(op->ptr, "ymin");
357         rect.xmax = RNA_int_get(op->ptr, "xmax");
358         rect.ymax = RNA_int_get(op->ptr, "ymax");
359
360         UI_view2d_region_to_view(&ar->v2d, rect.xmin, rect.ymin, &userdata.rect.xmin, &userdata.rect.ymin);
361         UI_view2d_region_to_view(&ar->v2d, rect.xmax, rect.ymax, &userdata.rect.xmax, &userdata.rect.ymax);
362
363         userdata.change = FALSE;
364         userdata.mode = RNA_int_get(op->ptr, "gesture_mode");
365         userdata.extend = RNA_boolean_get(op->ptr, "extend");
366
367         clip_graph_tracking_values_iterate_track(sc, act_track, &userdata, border_select_cb, NULL, NULL);
368
369         if (userdata.change) {
370                 WM_event_add_notifier(C, NC_GEOM | ND_SELECT, NULL);
371
372                 return OPERATOR_FINISHED;
373         }
374
375         return OPERATOR_CANCELLED;
376 }
377
378 void CLIP_OT_graph_select_border(wmOperatorType *ot)
379 {
380         /* identifiers */
381         ot->name = "Border Select";
382         ot->description = "Select curve points using border selection";
383         ot->idname = "CLIP_OT_graph_select_border";
384
385         /* api callbacks */
386         ot->invoke = WM_border_select_invoke;
387         ot->exec = border_select_graph_exec;
388         ot->modal = WM_border_select_modal;
389         ot->poll = ED_space_clip_graph_poll;
390
391         /* flags */
392         ot->flag = OPTYPE_UNDO;
393
394         /* properties */
395         WM_operator_properties_gesture_border(ot, TRUE);
396 }
397
398 /********************** select all operator *********************/
399
400 static int graph_select_all_markers_exec(bContext *C, wmOperator *op)
401 {
402         SpaceClip *sc = CTX_wm_space_clip(C);
403         MovieClip *clip = ED_space_clip(sc);
404         MovieTracking *tracking = &clip->tracking;
405         MovieTrackingTrack *act_track = BKE_tracking_track_get_active(tracking);
406         MovieTrackingMarker *marker;
407         int action = RNA_enum_get(op->ptr, "action");
408         int a;
409
410         if (!act_track)
411                 return OPERATOR_CANCELLED;
412
413         if (action == SEL_TOGGLE) {
414                 action = SEL_SELECT;
415
416                 for (a = 0; a < act_track->markersnr; a++) {
417                         marker = &act_track->markers[a];
418
419                         if (marker->flag & MARKER_GRAPH_SEL) {
420                                 action = SEL_DESELECT;
421                                 break;
422                         }
423                 }
424         }
425
426         for (a = 0; a < act_track->markersnr; a++) {
427                 marker = &act_track->markers[a];
428
429                 switch (action) {
430                         case SEL_SELECT:
431                                 marker->flag |= MARKER_GRAPH_SEL;
432                                 break;
433                         case SEL_DESELECT:
434                                 marker->flag &= ~MARKER_GRAPH_SEL;
435                                 break;
436                         case SEL_INVERT:
437                                 marker->flag ^= MARKER_GRAPH_SEL;
438                                 break;
439                 }
440         }
441
442         WM_event_add_notifier(C, NC_GEOM | ND_SELECT, NULL);
443
444         return OPERATOR_FINISHED;
445 }
446
447 void CLIP_OT_graph_select_all_markers(wmOperatorType *ot)
448 {
449         /* identifiers */
450         ot->name = "(De)select All Markers";
451         ot->description = "Change selection of all markers of active track";
452         ot->idname = "CLIP_OT_graph_select_all_markers";
453
454         /* api callbacks */
455         ot->exec = graph_select_all_markers_exec;
456         ot->poll = ED_space_clip_graph_poll;
457
458         /* flags */
459         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
460
461         WM_operator_properties_select_all(ot);
462 }
463
464 /******************** delete curve operator ********************/
465
466 static int delete_curve_exec(bContext *C, wmOperator *UNUSED(op))
467 {
468         SpaceClip *sc = CTX_wm_space_clip(C);
469         MovieClip *clip = ED_space_clip(sc);
470         MovieTracking *tracking = &clip->tracking;
471         ListBase *tracksbase = BKE_tracking_get_active_tracks(tracking);
472         MovieTrackingTrack *act_track = BKE_tracking_track_get_active(tracking);
473
474         if (act_track)
475                 clip_delete_track(C, clip, tracksbase, act_track);
476
477         return OPERATOR_FINISHED;
478 }
479
480 void CLIP_OT_graph_delete_curve(wmOperatorType *ot)
481 {
482         /* identifiers */
483         ot->name = "Delete Curve";
484         ot->description = "Delete selected curves";
485         ot->idname = "CLIP_OT_graph_delete_curve";
486
487         /* api callbacks */
488         ot->invoke = WM_operator_confirm;
489         ot->exec = delete_curve_exec;
490         ot->poll = ED_space_clip_tracking_poll;
491
492         /* flags */
493         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
494 }
495
496 /******************** delete knot operator ********************/
497
498 static int delete_knot_exec(bContext *C, wmOperator *UNUSED(op))
499 {
500         SpaceClip *sc = CTX_wm_space_clip(C);
501         MovieClip *clip = ED_space_clip(sc);
502         MovieTracking *tracking = &clip->tracking;
503         ListBase *tracksbase = BKE_tracking_get_active_tracks(tracking);
504         MovieTrackingTrack *act_track = BKE_tracking_track_get_active(tracking);
505
506         if (act_track) {
507                 int a = 0;
508
509                 while (a < act_track->markersnr) {
510                         MovieTrackingMarker *marker = &act_track->markers[a];
511
512                         if (marker->flag & MARKER_GRAPH_SEL)
513                                 clip_delete_marker(C, clip, tracksbase, act_track, marker);
514                         else
515                                 a++;
516                 }
517         }
518
519         return OPERATOR_FINISHED;
520 }
521
522 void CLIP_OT_graph_delete_knot(wmOperatorType *ot)
523 {
524         /* identifiers */
525         ot->name = "Delete Knot";
526         ot->description = "Delete curve knots";
527         ot->idname = "CLIP_OT_graph_delete_knot";
528
529         /* api callbacks */
530         ot->exec = delete_knot_exec;
531         ot->poll = ED_space_clip_graph_poll;
532
533         /* flags */
534         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
535 }
536
537 /******************** view all operator ********************/
538
539 typedef struct {
540         float min, max;
541 } ViewAllUserData;
542
543 static void view_all_cb(void *userdata, MovieTrackingTrack *UNUSED(track), MovieTrackingMarker *UNUSED(marker),
544                         int UNUSED(coord), int UNUSED(scene_framenr), float val)
545 {
546         ViewAllUserData *data = (ViewAllUserData *) userdata;
547
548         if (val < data->min)
549                 data->min = val;
550
551         if (val > data->max)
552                 data->max = val;
553 }
554
555 static int view_all_exec(bContext *C, wmOperator *UNUSED(op))
556 {
557         Scene *scene = CTX_data_scene(C);
558         ARegion *ar = CTX_wm_region(C);
559         SpaceClip *sc = CTX_wm_space_clip(C);
560         View2D *v2d = &ar->v2d;
561         ViewAllUserData userdata;
562         float extra;
563
564         userdata.max = -FLT_MAX;
565         userdata.min = FLT_MAX;
566
567         clip_graph_tracking_values_iterate(sc, sc->flag & SC_SHOW_GRAPH_SEL_ONLY,
568                                            sc->flag & SC_SHOW_GRAPH_HIDDEN, &userdata,
569                                            view_all_cb, NULL, NULL);
570
571         /* set extents of view to start/end frames */
572         v2d->cur.xmin = (float) SFRA;
573         v2d->cur.xmax = (float) EFRA;
574
575         if (userdata.min < userdata.max) {
576                 v2d->cur.ymin = userdata.min;
577                 v2d->cur.ymax = userdata.max;
578         }
579         else {
580                 v2d->cur.ymin = -10;
581                 v2d->cur.ymax = 10;
582         }
583
584         /* we need an extra "buffer" factor on either side so that the endpoints are visible */
585         extra = 0.01f * (v2d->cur.xmax - v2d->cur.xmin);
586         v2d->cur.xmin -= extra;
587         v2d->cur.xmax += extra;
588
589         extra = 0.01f * (v2d->cur.ymax - v2d->cur.ymin);
590         v2d->cur.ymin -= extra;
591         v2d->cur.ymax += extra;
592
593         ED_region_tag_redraw(ar);
594
595         return OPERATOR_FINISHED;
596 }
597
598 void CLIP_OT_graph_view_all(wmOperatorType *ot)
599 {
600         /* identifiers */
601         ot->name = "View All";
602         ot->description = "View all curves in editor";
603         ot->idname = "CLIP_OT_graph_view_all";
604
605         /* api callbacks */
606         ot->exec = view_all_exec;
607         ot->poll = ED_space_clip_graph_poll;
608 }
609
610 /******************** jump to current frame operator ********************/
611
612 void ED_clip_graph_center_current_frame(Scene *scene, ARegion *ar)
613 {
614         View2D *v2d = &ar->v2d;
615         float extra = (v2d->cur.xmax - v2d->cur.xmin) / 2.0f;
616
617         /* set extents of view to start/end frames */
618         v2d->cur.xmin = (float)CFRA - extra;
619         v2d->cur.xmax = (float)CFRA + extra;
620 }
621
622 static int center_current_frame_exec(bContext *C, wmOperator *UNUSED(op))
623 {
624         Scene *scene = CTX_data_scene(C);
625         ARegion *ar = CTX_wm_region(C);
626
627         ED_clip_graph_center_current_frame(scene, ar);
628
629         ED_region_tag_redraw(ar);
630
631         return OPERATOR_FINISHED;
632 }
633
634 void CLIP_OT_graph_center_current_frame(wmOperatorType *ot)
635 {
636         /* identifiers */
637         ot->name = "Center Current Frame";
638         ot->description = "Scroll view so current frame would be centered";
639         ot->idname = "CLIP_OT_graph_center_current_frame";
640
641         /* api callbacks */
642         ot->exec = center_current_frame_exec;
643         ot->poll = ED_space_clip_graph_poll;
644 }
645
646 /********************** disable markers operator *********************/
647
648 static int graph_disable_markers_exec(bContext *C, wmOperator *op)
649 {
650         SpaceClip *sc = CTX_wm_space_clip(C);
651         MovieClip *clip = ED_space_clip(sc);
652         MovieTracking *tracking = &clip->tracking;
653         MovieTrackingTrack *act_track = BKE_tracking_track_get_active(tracking);
654         MovieTrackingMarker *marker;
655         int action = RNA_enum_get(op->ptr, "action");
656         int a;
657
658         if (!act_track || (act_track->flag & TRACK_LOCKED))
659                 return OPERATOR_CANCELLED;
660
661         for (a = 0; a < act_track->markersnr; a++) {
662                 marker = &act_track->markers[a];
663
664                 if (marker->flag & MARKER_GRAPH_SEL) {
665                         if (action == 0)
666                                 marker->flag |= MARKER_DISABLED;
667                         else if (action == 1)
668                                 marker->flag &= ~MARKER_DISABLED;
669                         else
670                                 marker->flag ^= MARKER_DISABLED;
671                 }
672         }
673
674         DAG_id_tag_update(&clip->id, 0);
675
676         WM_event_add_notifier(C, NC_MOVIECLIP | NA_EVALUATED, clip);
677
678         return OPERATOR_FINISHED;
679 }
680
681 void CLIP_OT_graph_disable_markers(wmOperatorType *ot)
682 {
683         static EnumPropertyItem actions_items[] = {
684                 {0, "DISABLE", 0, "Disable", "Disable selected markers"},
685                 {1, "ENABLE", 0, "Enable", "Enable selected markers"},
686                 {2, "TOGGLE", 0, "Toggle", "Toggle disabled flag for selected markers"},
687                 {0, NULL, 0, NULL, NULL}
688         };
689
690         /* identifiers */
691         ot->name = "Disable Markers";
692         ot->description = "Disable/enable selected markers";
693         ot->idname = "CLIP_OT_graph_disable_markers";
694
695         /* api callbacks */
696         ot->exec = graph_disable_markers_exec;
697         ot->poll = ED_space_clip_graph_poll;
698
699         /* flags */
700         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
701
702         /* properties */
703         RNA_def_enum(ot->srna, "action", actions_items, 0, "Action", "Disable action to execute");
704 }