Cleanup: style, use braces for editors
[blender.git] / source / blender / editors / space_clip / tracking_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) 2011 Blender Foundation.
17  * All rights reserved.
18  */
19
20 /** \file
21  * \ingroup spclip
22  */
23
24 #include "MEM_guardedalloc.h"
25
26 #include "DNA_screen_types.h"
27 #include "DNA_space_types.h"
28
29 #include "BLI_utildefines.h"
30 #include "BLI_ghash.h"
31 #include "BLI_math.h"
32 #include "BLI_blenlib.h"
33
34 #include "BKE_context.h"
35 #include "BKE_movieclip.h"
36 #include "BKE_tracking.h"
37 #include "BKE_report.h"
38 #include "BKE_sound.h"
39
40 #include "DEG_depsgraph.h"
41
42 #include "WM_api.h"
43 #include "WM_types.h"
44
45 #include "ED_screen.h"
46 #include "ED_clip.h"
47
48 #include "RNA_access.h"
49 #include "RNA_define.h"
50
51 #include "BLT_translation.h"
52
53 #include "clip_intern.h"
54 #include "tracking_ops_intern.h"
55
56 /********************** add marker operator *********************/
57
58 static bool add_marker(const bContext *C, float x, float y)
59 {
60   SpaceClip *sc = CTX_wm_space_clip(C);
61   MovieClip *clip = ED_space_clip_get_clip(sc);
62   MovieTracking *tracking = &clip->tracking;
63   ListBase *tracksbase = BKE_tracking_get_active_tracks(tracking);
64   ListBase *plane_tracks_base = BKE_tracking_get_active_plane_tracks(tracking);
65   MovieTrackingTrack *track;
66   int width, height;
67   int framenr = ED_space_clip_get_clip_frame_number(sc);
68
69   ED_space_clip_get_size(sc, &width, &height);
70
71   if (width == 0 || height == 0) {
72     return false;
73   }
74
75   track = BKE_tracking_track_add(tracking, tracksbase, x, y, framenr, width, height);
76
77   BKE_tracking_track_select(tracksbase, track, TRACK_AREA_ALL, 0);
78   BKE_tracking_plane_tracks_deselect_all(plane_tracks_base);
79
80   clip->tracking.act_track = track;
81   clip->tracking.act_plane_track = NULL;
82
83   return true;
84 }
85
86 static int add_marker_exec(bContext *C, wmOperator *op)
87 {
88   SpaceClip *sc = CTX_wm_space_clip(C);
89   MovieClip *clip = ED_space_clip_get_clip(sc);
90   float pos[2];
91
92   RNA_float_get_array(op->ptr, "location", pos);
93
94   if (!add_marker(C, pos[0], pos[1])) {
95     return OPERATOR_CANCELLED;
96   }
97
98   /* Reset offset from locked position, so frame jumping wouldn't be so
99    * confusing.
100    */
101   sc->xlockof = 0;
102   sc->ylockof = 0;
103
104   WM_event_add_notifier(C, NC_MOVIECLIP | NA_EDITED, clip);
105
106   return OPERATOR_FINISHED;
107 }
108
109 static int add_marker_invoke(bContext *C, wmOperator *op, const wmEvent *event)
110 {
111   SpaceClip *sc = CTX_wm_space_clip(C);
112   ARegion *ar = CTX_wm_region(C);
113
114   if (!RNA_struct_property_is_set(op->ptr, "location")) {
115     /* If location is not set, use mouse positio nas default. */
116     float co[2];
117     ED_clip_mouse_pos(sc, ar, event->mval, co);
118     RNA_float_set_array(op->ptr, "location", co);
119   }
120
121   return add_marker_exec(C, op);
122 }
123
124 void CLIP_OT_add_marker(wmOperatorType *ot)
125 {
126   /* identifiers */
127   ot->name = "Add Marker";
128   ot->idname = "CLIP_OT_add_marker";
129   ot->description = "Place new marker at specified location";
130
131   /* api callbacks */
132   ot->invoke = add_marker_invoke;
133   ot->exec = add_marker_exec;
134   ot->poll = ED_space_clip_tracking_poll;
135
136   /* flags */
137   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
138
139   /* properties */
140   RNA_def_float_vector(ot->srna,
141                        "location",
142                        2,
143                        NULL,
144                        -FLT_MAX,
145                        FLT_MAX,
146                        "Location",
147                        "Location of marker on frame",
148                        -1.0f,
149                        1.0f);
150 }
151
152 /********************** add marker operator *********************/
153
154 static int add_marker_at_click_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
155 {
156   ED_workspace_status_text(C, IFACE_("Use LMB click to define location where place the marker"));
157
158   /* Add modal handler for ESC. */
159   WM_event_add_modal_handler(C, op);
160
161   return OPERATOR_RUNNING_MODAL;
162 }
163
164 static int add_marker_at_click_modal(bContext *C, wmOperator *UNUSED(op), const wmEvent *event)
165 {
166   switch (event->type) {
167     case MOUSEMOVE:
168       return OPERATOR_RUNNING_MODAL;
169
170     case LEFTMOUSE: {
171       SpaceClip *sc = CTX_wm_space_clip(C);
172       MovieClip *clip = ED_space_clip_get_clip(sc);
173       ARegion *ar = CTX_wm_region(C);
174       float pos[2];
175
176       ED_workspace_status_text(C, NULL);
177
178       ED_clip_point_stable_pos(
179           sc, ar, event->x - ar->winrct.xmin, event->y - ar->winrct.ymin, &pos[0], &pos[1]);
180
181       if (!add_marker(C, pos[0], pos[1])) {
182         return OPERATOR_CANCELLED;
183       }
184
185       WM_event_add_notifier(C, NC_MOVIECLIP | NA_EDITED, clip);
186       return OPERATOR_FINISHED;
187     }
188
189     case ESCKEY:
190       ED_workspace_status_text(C, NULL);
191       return OPERATOR_CANCELLED;
192   }
193
194   return OPERATOR_PASS_THROUGH;
195 }
196
197 void CLIP_OT_add_marker_at_click(wmOperatorType *ot)
198 {
199   /* identifiers */
200   ot->name = "Add Marker at Click";
201   ot->idname = "CLIP_OT_add_marker_at_click";
202   ot->description = "Place new marker at the desired (clicked) position";
203
204   /* api callbacks */
205   ot->invoke = add_marker_at_click_invoke;
206   ot->poll = ED_space_clip_tracking_poll;
207   ot->modal = add_marker_at_click_modal;
208
209   /* flags */
210   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING;
211 }
212
213 /********************** delete track operator *********************/
214
215 static int delete_track_exec(bContext *C, wmOperator *UNUSED(op))
216 {
217   SpaceClip *sc = CTX_wm_space_clip(C);
218   MovieClip *clip = ED_space_clip_get_clip(sc);
219   MovieTracking *tracking = &clip->tracking;
220   bool changed = false;
221   /* Delete selected plane tracks. */
222   ListBase *plane_tracks_base = BKE_tracking_get_active_plane_tracks(tracking);
223   for (MovieTrackingPlaneTrack *plane_track = plane_tracks_base->first, *next_plane_track;
224        plane_track != NULL;
225        plane_track = next_plane_track) {
226     next_plane_track = plane_track->next;
227     if (PLANE_TRACK_VIEW_SELECTED(plane_track)) {
228       clip_delete_plane_track(C, clip, plane_track);
229       changed = true;
230     }
231   }
232   /* Remove selected point tracks (they'll also be removed from planes which
233    * uses them).
234    */
235   ListBase *tracksbase = BKE_tracking_get_active_tracks(tracking);
236   for (MovieTrackingTrack *track = tracksbase->first, *next_track; track != NULL;
237        track = next_track) {
238     next_track = track->next;
239     if (TRACK_VIEW_SELECTED(sc, track)) {
240       clip_delete_track(C, clip, track);
241       changed = true;
242     }
243   }
244   /* Nothing selected now, unlock view so it can be scrolled nice again. */
245   sc->flag &= ~SC_LOCK_SELECTION;
246   if (changed) {
247     WM_event_add_notifier(C, NC_MOVIECLIP | NA_EDITED, clip);
248   }
249   return OPERATOR_FINISHED;
250 }
251
252 void CLIP_OT_delete_track(wmOperatorType *ot)
253 {
254   /* identifiers */
255   ot->name = "Delete Track";
256   ot->idname = "CLIP_OT_delete_track";
257   ot->description = "Delete selected tracks";
258
259   /* api callbacks */
260   ot->invoke = WM_operator_confirm;
261   ot->exec = delete_track_exec;
262   ot->poll = ED_space_clip_tracking_poll;
263
264   /* flags */
265   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
266 }
267
268 /********************** delete marker operator *********************/
269
270 static int delete_marker_exec(bContext *C, wmOperator *UNUSED(op))
271 {
272   SpaceClip *sc = CTX_wm_space_clip(C);
273   MovieClip *clip = ED_space_clip_get_clip(sc);
274   MovieTracking *tracking = &clip->tracking;
275   const int framenr = ED_space_clip_get_clip_frame_number(sc);
276   bool has_selection = false;
277   bool changed = false;
278
279   ListBase *tracksbase = BKE_tracking_get_active_tracks(tracking);
280   for (MovieTrackingTrack *track = tracksbase->first, *next_track; track != NULL;
281        track = next_track) {
282     next_track = track->next;
283     if (TRACK_VIEW_SELECTED(sc, track)) {
284       MovieTrackingMarker *marker = BKE_tracking_marker_get_exact(track, framenr);
285       if (marker != NULL) {
286         has_selection |= track->markersnr > 1;
287         clip_delete_marker(C, clip, track, marker);
288         changed = true;
289       }
290     }
291   }
292
293   ListBase *plane_tracks_base = BKE_tracking_get_active_plane_tracks(tracking);
294   for (MovieTrackingPlaneTrack *plane_track = plane_tracks_base->first, *plane_track_next;
295        plane_track != NULL;
296        plane_track = plane_track_next) {
297     plane_track_next = plane_track->next;
298     if (PLANE_TRACK_VIEW_SELECTED(plane_track)) {
299       MovieTrackingPlaneMarker *plane_marker = BKE_tracking_plane_marker_get_exact(plane_track,
300                                                                                    framenr);
301       if (plane_marker != NULL) {
302         if (plane_track->markersnr == 1) {
303           BKE_tracking_plane_track_free(plane_track);
304           BLI_freelinkN(plane_tracks_base, plane_track);
305         }
306         else {
307           BKE_tracking_plane_marker_delete(plane_track, framenr);
308         }
309         changed = true;
310       }
311     }
312   }
313
314   if (!has_selection) {
315     /* Nothing selected now, unlock view so it can be scrolled nice again. */
316     sc->flag &= ~SC_LOCK_SELECTION;
317   }
318
319   if (!changed) {
320     return OPERATOR_CANCELLED;
321   }
322
323   return OPERATOR_FINISHED;
324 }
325
326 void CLIP_OT_delete_marker(wmOperatorType *ot)
327 {
328   /* identifiers */
329   ot->name = "Delete Marker";
330   ot->idname = "CLIP_OT_delete_marker";
331   ot->description = "Delete marker for current frame from selected tracks";
332
333   /* api callbacks */
334   ot->invoke = WM_operator_confirm;
335   ot->exec = delete_marker_exec;
336   ot->poll = ED_space_clip_tracking_poll;
337
338   /* flags */
339   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
340 }
341
342 /********************** slide marker operator *********************/
343
344 enum {
345   SLIDE_ACTION_POS = 0,
346   SLIDE_ACTION_SIZE,
347   SLIDE_ACTION_OFFSET,
348   SLIDE_ACTION_TILT_SIZE,
349 };
350
351 typedef struct {
352   short area, action;
353   MovieTrackingTrack *track;
354   MovieTrackingMarker *marker;
355
356   int mval[2];
357   int width, height;
358   float *min, *max, *pos, *offset, (*corners)[2];
359   float spos[2];
360
361   bool lock, accurate;
362
363   /* Data to restore on cancel. */
364   float old_search_min[2], old_search_max[2], old_pos[2], old_offset[2];
365   float old_corners[4][2];
366   float (*old_markers)[2];
367 } SlideMarkerData;
368
369 static void slide_marker_tilt_slider(const MovieTrackingMarker *marker, float r_slider[2])
370 {
371   add_v2_v2v2(r_slider, marker->pattern_corners[1], marker->pattern_corners[2]);
372   add_v2_v2(r_slider, marker->pos);
373 }
374
375 static SlideMarkerData *create_slide_marker_data(SpaceClip *sc,
376                                                  MovieTrackingTrack *track,
377                                                  MovieTrackingMarker *marker,
378                                                  const wmEvent *event,
379                                                  int area,
380                                                  int corner,
381                                                  int action,
382                                                  int width,
383                                                  int height)
384 {
385   SlideMarkerData *data = MEM_callocN(sizeof(SlideMarkerData), "slide marker data");
386   int framenr = ED_space_clip_get_clip_frame_number(sc);
387
388   marker = BKE_tracking_marker_ensure(track, framenr);
389
390   data->area = area;
391   data->action = action;
392   data->track = track;
393   data->marker = marker;
394
395   if (area == TRACK_AREA_POINT) {
396     data->pos = marker->pos;
397     data->offset = track->offset;
398   }
399   else if (area == TRACK_AREA_PAT) {
400     if (action == SLIDE_ACTION_SIZE) {
401       data->corners = marker->pattern_corners;
402     }
403     else if (action == SLIDE_ACTION_OFFSET) {
404       data->pos = marker->pos;
405       data->offset = track->offset;
406       data->old_markers = MEM_callocN(sizeof(*data->old_markers) * track->markersnr,
407                                       "slide marekrs");
408       for (int a = 0; a < track->markersnr; a++) {
409         copy_v2_v2(data->old_markers[a], track->markers[a].pos);
410       }
411     }
412     else if (action == SLIDE_ACTION_POS) {
413       data->corners = marker->pattern_corners;
414       data->pos = marker->pattern_corners[corner];
415       copy_v2_v2(data->spos, data->pos);
416     }
417     else if (action == SLIDE_ACTION_TILT_SIZE) {
418       data->corners = marker->pattern_corners;
419       slide_marker_tilt_slider(marker, data->spos);
420     }
421   }
422   else if (area == TRACK_AREA_SEARCH) {
423     data->min = marker->search_min;
424     data->max = marker->search_max;
425   }
426
427   data->mval[0] = event->mval[0];
428   data->mval[1] = event->mval[1];
429
430   data->width = width;
431   data->height = height;
432
433   if (action == SLIDE_ACTION_SIZE) {
434     data->lock = true;
435   }
436
437   /* Backup marker's settings. */
438   memcpy(data->old_corners, marker->pattern_corners, sizeof(data->old_corners));
439   copy_v2_v2(data->old_search_min, marker->search_min);
440   copy_v2_v2(data->old_search_max, marker->search_max);
441   copy_v2_v2(data->old_pos, marker->pos);
442   copy_v2_v2(data->old_offset, track->offset);
443
444   return data;
445 }
446
447 static float mouse_to_slide_zone_distance_squared(const float co[2],
448                                                   const float slide_zone[2],
449                                                   int width,
450                                                   int height)
451 {
452   float pixel_co[2] = {co[0] * width, co[1] * height},
453         pixel_slide_zone[2] = {slide_zone[0] * width, slide_zone[1] * height};
454   return SQUARE(pixel_co[0] - pixel_slide_zone[0]) + SQUARE(pixel_co[1] - pixel_slide_zone[1]);
455 }
456
457 static float mouse_to_search_corner_distance_squared(
458     const MovieTrackingMarker *marker, const float co[2], int corner, int width, int height)
459 {
460   float side_zone[2];
461   if (corner == 0) {
462     side_zone[0] = marker->pos[0] + marker->search_max[0];
463     side_zone[1] = marker->pos[1] + marker->search_min[1];
464   }
465   else {
466     side_zone[0] = marker->pos[0] + marker->search_min[0];
467     side_zone[1] = marker->pos[1] + marker->search_max[1];
468   }
469   return mouse_to_slide_zone_distance_squared(co, side_zone, width, height);
470 }
471
472 static float mouse_to_closest_pattern_corner_distance_squared(
473     const MovieTrackingMarker *marker, const float co[2], int width, int height, int *r_corner)
474 {
475   float min_distance_squared = FLT_MAX;
476   for (int i = 0; i < 4; i++) {
477     float corner_co[2];
478     add_v2_v2v2(corner_co, marker->pattern_corners[i], marker->pos);
479     float distance_squared = mouse_to_slide_zone_distance_squared(co, corner_co, width, height);
480     if (distance_squared < min_distance_squared) {
481       min_distance_squared = distance_squared;
482       *r_corner = i;
483     }
484   }
485   return min_distance_squared;
486 }
487
488 static float mouse_to_offset_distance_squared(const MovieTrackingTrack *track,
489                                               const MovieTrackingMarker *marker,
490                                               const float co[2],
491                                               int width,
492                                               int height)
493 {
494   float pos[2];
495   add_v2_v2v2(pos, marker->pos, track->offset);
496   return mouse_to_slide_zone_distance_squared(co, pos, width, height);
497 }
498
499 static int mouse_to_tilt_distance_squared(const MovieTrackingMarker *marker,
500                                           const float co[2],
501                                           int width,
502                                           int height)
503 {
504   float slider[2];
505   slide_marker_tilt_slider(marker, slider);
506   return mouse_to_slide_zone_distance_squared(co, slider, width, height);
507 }
508
509 static bool slide_check_corners(float (*corners)[2])
510 {
511   int i, next, prev;
512   float cross = 0.0f;
513   float p[2] = {0.0f, 0.0f};
514
515   if (!isect_point_quad_v2(p, corners[0], corners[1], corners[2], corners[3])) {
516     return false;
517   }
518
519   for (i = 0; i < 4; i++) {
520     float v1[2], v2[2], cur_cross;
521
522     next = (i + 1) % 4;
523     prev = (4 + i - 1) % 4;
524
525     sub_v2_v2v2(v1, corners[i], corners[prev]);
526     sub_v2_v2v2(v2, corners[next], corners[i]);
527
528     cur_cross = cross_v2v2(v1, v2);
529
530     if (fabsf(cur_cross) > FLT_EPSILON) {
531       if (cross == 0.0f) {
532         cross = cur_cross;
533       }
534       else if (cross * cur_cross < 0.0f) {
535         return false;
536       }
537     }
538   }
539
540   return true;
541 }
542
543 MovieTrackingTrack *tracking_marker_check_slide(
544     bContext *C, const wmEvent *event, int *area_r, int *action_r, int *corner_r)
545 {
546   const float distance_clip_squared = 12.0f * 12.0f;
547   SpaceClip *sc = CTX_wm_space_clip(C);
548   ARegion *ar = CTX_wm_region(C);
549
550   MovieClip *clip = ED_space_clip_get_clip(sc);
551   MovieTrackingTrack *track;
552   int width, height;
553   float co[2];
554   ListBase *tracksbase = BKE_tracking_get_active_tracks(&clip->tracking);
555   int framenr = ED_space_clip_get_clip_frame_number(sc);
556   float global_min_distance_squared = FLT_MAX;
557
558   /* Sliding zone designator which is the closest to the mouse
559    * across all the tracks.
560    */
561   int min_action = -1, min_area = 0, min_corner = -1;
562   MovieTrackingTrack *min_track = NULL;
563
564   ED_space_clip_get_size(sc, &width, &height);
565
566   if (width == 0 || height == 0) {
567     return NULL;
568   }
569
570   ED_clip_mouse_pos(sc, ar, event->mval, co);
571
572   track = tracksbase->first;
573   while (track) {
574     if (TRACK_VIEW_SELECTED(sc, track) && (track->flag & TRACK_LOCKED) == 0) {
575       const MovieTrackingMarker *marker = BKE_tracking_marker_get(track, framenr);
576       /* Sliding zone designator which is the closest to the mouse for
577        * the current tracks.
578        */
579       float min_distance_squared = FLT_MAX;
580       int action = -1, area = 0, corner = -1;
581
582       if ((marker->flag & MARKER_DISABLED) == 0) {
583         float distance_squared;
584
585         /* We start checking with whether the mouse is close enough
586          * to the pattern offset area.
587          */
588         distance_squared = mouse_to_offset_distance_squared(track, marker, co, width, height);
589         area = TRACK_AREA_POINT;
590         action = SLIDE_ACTION_POS;
591
592         /* NOTE: All checks here are assuming there's no maximum distance
593          * limit, so checks are quite simple here.
594          * Actual distance clipping happens later once all the sliding
595          * zones are checked.
596          */
597         min_distance_squared = distance_squared;
598
599         /* If search area is visible, check how close to it's sliding
600          * zones mouse is.
601          */
602         if (sc->flag & SC_SHOW_MARKER_SEARCH) {
603           distance_squared = mouse_to_search_corner_distance_squared(marker, co, 1, width, height);
604           if (distance_squared < min_distance_squared) {
605             area = TRACK_AREA_SEARCH;
606             action = SLIDE_ACTION_OFFSET;
607             min_distance_squared = distance_squared;
608           }
609
610           distance_squared = mouse_to_search_corner_distance_squared(marker, co, 0, width, height);
611           if (distance_squared < min_distance_squared) {
612             area = TRACK_AREA_SEARCH;
613             action = SLIDE_ACTION_SIZE;
614             min_distance_squared = distance_squared;
615           }
616         }
617
618         /* If pattern area is visible, check which corner is closest to
619          * the mouse.
620          */
621         if (sc->flag & SC_SHOW_MARKER_PATTERN) {
622           int current_corner = -1;
623           distance_squared = mouse_to_closest_pattern_corner_distance_squared(
624               marker, co, width, height, &current_corner);
625           if (distance_squared < min_distance_squared) {
626             area = TRACK_AREA_PAT;
627             action = SLIDE_ACTION_POS;
628             corner = current_corner;
629             min_distance_squared = distance_squared;
630           }
631
632           /* Here we also check whether the mouse is actually closer to
633            * the widget which controls scale and tilt.
634            */
635           distance_squared = mouse_to_tilt_distance_squared(marker, co, width, height);
636           if (distance_squared < min_distance_squared) {
637             area = TRACK_AREA_PAT;
638             action = SLIDE_ACTION_TILT_SIZE;
639             min_distance_squared = distance_squared;
640           }
641         }
642
643         if (min_distance_squared < global_min_distance_squared) {
644           min_area = area;
645           min_action = action;
646           min_corner = corner;
647           min_track = track;
648           global_min_distance_squared = min_distance_squared;
649         }
650       }
651     }
652
653     track = track->next;
654   }
655
656   if (global_min_distance_squared < distance_clip_squared / sc->zoom) {
657     if (area_r) {
658       *area_r = min_area;
659     }
660     if (action_r) {
661       *action_r = min_action;
662     }
663     if (corner_r) {
664       *corner_r = min_corner;
665     }
666     return min_track;
667   }
668   return NULL;
669 }
670
671 static void *slide_marker_customdata(bContext *C, const wmEvent *event)
672 {
673   SpaceClip *sc = CTX_wm_space_clip(C);
674   ARegion *ar = CTX_wm_region(C);
675
676   MovieTrackingTrack *track;
677   int width, height;
678   float co[2];
679   void *customdata = NULL;
680   int framenr = ED_space_clip_get_clip_frame_number(sc);
681   int area, action, corner;
682
683   ED_space_clip_get_size(sc, &width, &height);
684
685   if (width == 0 || height == 0) {
686     return NULL;
687   }
688
689   ED_clip_mouse_pos(sc, ar, event->mval, co);
690
691   track = tracking_marker_check_slide(C, event, &area, &action, &corner);
692   if (track != NULL) {
693     MovieTrackingMarker *marker = BKE_tracking_marker_get(track, framenr);
694     customdata = create_slide_marker_data(
695         sc, track, marker, event, area, corner, action, width, height);
696   }
697
698   return customdata;
699 }
700
701 static int slide_marker_invoke(bContext *C, wmOperator *op, const wmEvent *event)
702 {
703   SlideMarkerData *slidedata = slide_marker_customdata(C, event);
704   if (slidedata != NULL) {
705     SpaceClip *sc = CTX_wm_space_clip(C);
706     MovieClip *clip = ED_space_clip_get_clip(sc);
707     MovieTracking *tracking = &clip->tracking;
708
709     tracking->act_track = slidedata->track;
710     tracking->act_plane_track = NULL;
711
712     op->customdata = slidedata;
713
714     clip_tracking_hide_cursor(C);
715     WM_event_add_modal_handler(C, op);
716
717     WM_event_add_notifier(C, NC_GEOM | ND_SELECT, NULL);
718
719     return OPERATOR_RUNNING_MODAL;
720   }
721
722   return OPERATOR_PASS_THROUGH;
723 }
724
725 static void cancel_mouse_slide(SlideMarkerData *data)
726 {
727   MovieTrackingTrack *track = data->track;
728   MovieTrackingMarker *marker = data->marker;
729
730   memcpy(marker->pattern_corners, data->old_corners, sizeof(marker->pattern_corners));
731   copy_v2_v2(marker->search_min, data->old_search_min);
732   copy_v2_v2(marker->search_max, data->old_search_max);
733   copy_v2_v2(marker->pos, data->old_pos);
734   copy_v2_v2(track->offset, data->old_offset);
735
736   if (data->old_markers != NULL) {
737     for (int a = 0; a < data->track->markersnr; a++) {
738       copy_v2_v2(data->track->markers[a].pos, data->old_markers[a]);
739     }
740   }
741 }
742
743 static void apply_mouse_slide(bContext *C, SlideMarkerData *data)
744 {
745   if (data->area == TRACK_AREA_POINT) {
746     SpaceClip *sc = CTX_wm_space_clip(C);
747     MovieClip *clip = ED_space_clip_get_clip(sc);
748     MovieTracking *tracking = &clip->tracking;
749     ListBase *plane_tracks_base = BKE_tracking_get_active_plane_tracks(tracking);
750     int framenr = ED_space_clip_get_clip_frame_number(sc);
751
752     for (MovieTrackingPlaneTrack *plane_track = plane_tracks_base->first; plane_track != NULL;
753          plane_track = plane_track->next) {
754       if ((plane_track->flag & PLANE_TRACK_AUTOKEY) == 0) {
755         if (BKE_tracking_plane_track_has_point_track(plane_track, data->track)) {
756           BKE_tracking_track_plane_from_existing_motion(plane_track, framenr);
757         }
758       }
759     }
760   }
761 }
762
763 static void free_slide_data(SlideMarkerData *data)
764 {
765   if (data->old_markers != NULL) {
766     MEM_freeN(data->old_markers);
767   }
768   MEM_freeN(data);
769 }
770
771 static int slide_marker_modal(bContext *C, wmOperator *op, const wmEvent *event)
772 {
773   SpaceClip *sc = CTX_wm_space_clip(C);
774   ARegion *ar = CTX_wm_region(C);
775
776   SlideMarkerData *data = (SlideMarkerData *)op->customdata;
777   float dx, dy, mdelta[2];
778
779   switch (event->type) {
780     case LEFTCTRLKEY:
781     case RIGHTCTRLKEY:
782     case LEFTSHIFTKEY:
783     case RIGHTSHIFTKEY:
784       if (data->action == SLIDE_ACTION_SIZE) {
785         if (ELEM(event->type, LEFTCTRLKEY, RIGHTCTRLKEY)) {
786           data->lock = event->val == KM_RELEASE;
787         }
788       }
789
790       if (ELEM(event->type, LEFTSHIFTKEY, RIGHTSHIFTKEY)) {
791         data->accurate = event->val == KM_PRESS;
792       }
793       ATTR_FALLTHROUGH;
794     case MOUSEMOVE:
795       mdelta[0] = event->mval[0] - data->mval[0];
796       mdelta[1] = event->mval[1] - data->mval[1];
797
798       dx = mdelta[0] / data->width / sc->zoom;
799
800       if (data->lock) {
801         dy = -dx / data->height * data->width;
802       }
803       else {
804         dy = mdelta[1] / data->height / sc->zoom;
805       }
806
807       if (data->accurate) {
808         dx /= 5.0f;
809         dy /= 5.0f;
810       }
811
812       if (data->area == TRACK_AREA_POINT) {
813         if (data->action == SLIDE_ACTION_OFFSET) {
814           data->offset[0] = data->old_offset[0] + dx;
815           data->offset[1] = data->old_offset[1] + dy;
816         }
817         else {
818           data->pos[0] = data->old_pos[0] + dx;
819           data->pos[1] = data->old_pos[1] + dy;
820         }
821
822         WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, NULL);
823         DEG_id_tag_update(&sc->clip->id, 0);
824       }
825       else if (data->area == TRACK_AREA_PAT) {
826         if (data->action == SLIDE_ACTION_SIZE) {
827           float start[2], end[2];
828           float scale;
829
830           ED_clip_point_stable_pos(sc, ar, data->mval[0], data->mval[1], &start[0], &start[1]);
831
832           sub_v2_v2(start, data->old_pos);
833
834           if (len_squared_v2(start) != 0.0f) {
835             float mval[2];
836
837             if (data->accurate) {
838               mval[0] = data->mval[0] + (event->mval[0] - data->mval[0]) / 5.0f;
839               mval[1] = data->mval[1] + (event->mval[1] - data->mval[1]) / 5.0f;
840             }
841             else {
842               mval[0] = event->mval[0];
843               mval[1] = event->mval[1];
844             }
845
846             ED_clip_point_stable_pos(sc, ar, mval[0], mval[1], &end[0], &end[1]);
847
848             sub_v2_v2(end, data->old_pos);
849             scale = len_v2(end) / len_v2(start);
850
851             if (scale > 0.0f) {
852               for (int a = 0; a < 4; a++) {
853                 mul_v2_v2fl(data->corners[a], data->old_corners[a], scale);
854               }
855             }
856           }
857
858           BKE_tracking_marker_clamp(data->marker, CLAMP_PAT_DIM);
859         }
860         else if (data->action == SLIDE_ACTION_OFFSET) {
861           float d[2] = {dx, dy};
862           for (int a = 0; a < data->track->markersnr; a++) {
863             add_v2_v2v2(data->track->markers[a].pos, data->old_markers[a], d);
864           }
865           sub_v2_v2v2(data->offset, data->old_offset, d);
866         }
867         else if (data->action == SLIDE_ACTION_POS) {
868           float spos[2];
869
870           copy_v2_v2(spos, data->pos);
871
872           data->pos[0] = data->spos[0] + dx;
873           data->pos[1] = data->spos[1] + dy;
874
875           if (!slide_check_corners(data->corners)) {
876             copy_v2_v2(data->pos, spos);
877           }
878
879           /* Currently only patterns are allowed to have such
880            * combination of event and data.
881            */
882           BKE_tracking_marker_clamp(data->marker, CLAMP_PAT_DIM);
883         }
884         else if (data->action == SLIDE_ACTION_TILT_SIZE) {
885           float start[2], end[2];
886           float scale = 1.0f, angle = 0.0f;
887           float mval[2];
888
889           if (data->accurate) {
890             mval[0] = data->mval[0] + (event->mval[0] - data->mval[0]) / 5.0f;
891             mval[1] = data->mval[1] + (event->mval[1] - data->mval[1]) / 5.0f;
892           }
893           else {
894             mval[0] = event->mval[0];
895             mval[1] = event->mval[1];
896           }
897
898           sub_v2_v2v2(start, data->spos, data->old_pos);
899
900           ED_clip_point_stable_pos(sc, ar, mval[0], mval[1], &end[0], &end[1]);
901           sub_v2_v2(end, data->old_pos);
902
903           if (len_squared_v2(start) != 0.0f) {
904             scale = len_v2(end) / len_v2(start);
905
906             if (scale < 0.0f) {
907               scale = 0.0;
908             }
909           }
910
911           angle = -angle_signed_v2v2(start, end);
912
913           for (int a = 0; a < 4; a++) {
914             float vec[2];
915
916             mul_v2_v2fl(data->corners[a], data->old_corners[a], scale);
917
918             copy_v2_v2(vec, data->corners[a]);
919             vec[0] *= data->width;
920             vec[1] *= data->height;
921
922             data->corners[a][0] = (vec[0] * cosf(angle) - vec[1] * sinf(angle)) / data->width;
923             data->corners[a][1] = (vec[1] * cosf(angle) + vec[0] * sinf(angle)) / data->height;
924           }
925
926           BKE_tracking_marker_clamp(data->marker, CLAMP_PAT_DIM);
927         }
928       }
929       else if (data->area == TRACK_AREA_SEARCH) {
930         if (data->action == SLIDE_ACTION_SIZE) {
931           data->min[0] = data->old_search_min[0] - dx;
932           data->max[0] = data->old_search_max[0] + dx;
933
934           data->min[1] = data->old_search_min[1] + dy;
935           data->max[1] = data->old_search_max[1] - dy;
936
937           BKE_tracking_marker_clamp(data->marker, CLAMP_SEARCH_DIM);
938         }
939         else if (data->area == TRACK_AREA_SEARCH) {
940           float d[2] = {dx, dy};
941           add_v2_v2v2(data->min, data->old_search_min, d);
942           add_v2_v2v2(data->max, data->old_search_max, d);
943         }
944
945         BKE_tracking_marker_clamp(data->marker, CLAMP_SEARCH_POS);
946       }
947
948       data->marker->flag &= ~MARKER_TRACKED;
949
950       WM_event_add_notifier(C, NC_MOVIECLIP | NA_EDITED, NULL);
951
952       break;
953
954     case LEFTMOUSE:
955       if (event->val == KM_RELEASE) {
956         apply_mouse_slide(C, op->customdata);
957         free_slide_data(op->customdata);
958
959         clip_tracking_show_cursor(C);
960
961         return OPERATOR_FINISHED;
962       }
963
964       break;
965
966     case ESCKEY:
967       cancel_mouse_slide(op->customdata);
968
969       free_slide_data(op->customdata);
970
971       clip_tracking_show_cursor(C);
972
973       WM_event_add_notifier(C, NC_MOVIECLIP | NA_EDITED, NULL);
974
975       return OPERATOR_CANCELLED;
976   }
977
978   return OPERATOR_RUNNING_MODAL;
979 }
980
981 void CLIP_OT_slide_marker(wmOperatorType *ot)
982 {
983   /* identifiers */
984   ot->name = "Slide Marker";
985   ot->description = "Slide marker areas";
986   ot->idname = "CLIP_OT_slide_marker";
987
988   /* api callbacks */
989   ot->poll = ED_space_clip_tracking_poll;
990   ot->invoke = slide_marker_invoke;
991   ot->modal = slide_marker_modal;
992
993   /* flags */
994   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_GRAB_CURSOR | OPTYPE_BLOCKING;
995
996   /* properties */
997   RNA_def_float_vector(ot->srna,
998                        "offset",
999                        2,
1000                        NULL,
1001                        -FLT_MAX,
1002                        FLT_MAX,
1003                        "Offset",
1004                        "Offset in floating point units, 1.0 is the width and height of the image",
1005                        -FLT_MAX,
1006                        FLT_MAX);
1007 }
1008
1009 /********************** clear track operator *********************/
1010
1011 static int clear_track_path_exec(bContext *C, wmOperator *op)
1012 {
1013   SpaceClip *sc = CTX_wm_space_clip(C);
1014   MovieClip *clip = ED_space_clip_get_clip(sc);
1015   MovieTracking *tracking = &clip->tracking;
1016   ListBase *tracksbase = BKE_tracking_get_active_tracks(tracking);
1017   int action = RNA_enum_get(op->ptr, "action");
1018   const bool clear_active = RNA_boolean_get(op->ptr, "clear_active");
1019   int framenr = ED_space_clip_get_clip_frame_number(sc);
1020
1021   if (clear_active) {
1022     MovieTrackingTrack *track = BKE_tracking_track_get_active(tracking);
1023     if (track != NULL) {
1024       BKE_tracking_track_path_clear(track, framenr, action);
1025     }
1026   }
1027   else {
1028     for (MovieTrackingTrack *track = tracksbase->first; track != NULL; track = track->next) {
1029       if (TRACK_VIEW_SELECTED(sc, track)) {
1030         BKE_tracking_track_path_clear(track, framenr, action);
1031       }
1032     }
1033   }
1034
1035   BKE_tracking_dopesheet_tag_update(tracking);
1036   WM_event_add_notifier(C, NC_MOVIECLIP | NA_EVALUATED, clip);
1037
1038   return OPERATOR_FINISHED;
1039 }
1040
1041 void CLIP_OT_clear_track_path(wmOperatorType *ot)
1042 {
1043   static const EnumPropertyItem clear_path_actions[] = {
1044       {TRACK_CLEAR_UPTO, "UPTO", 0, "Clear up-to", "Clear path up to current frame"},
1045       {TRACK_CLEAR_REMAINED,
1046        "REMAINED",
1047        0,
1048        "Clear remained",
1049        "Clear path at remaining frames (after current)"},
1050       {TRACK_CLEAR_ALL, "ALL", 0, "Clear all", "Clear the whole path"},
1051       {0, NULL, 0, NULL, NULL},
1052   };
1053
1054   /* identifiers */
1055   ot->name = "Clear Track Path";
1056   ot->description = "Clear tracks after/before current position or clear the whole track";
1057   ot->idname = "CLIP_OT_clear_track_path";
1058
1059   /* api callbacks */
1060   ot->exec = clear_track_path_exec;
1061   ot->poll = ED_space_clip_tracking_poll;
1062
1063   /* flags */
1064   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1065
1066   /* properties */
1067   RNA_def_enum(ot->srna,
1068                "action",
1069                clear_path_actions,
1070                TRACK_CLEAR_REMAINED,
1071                "Action",
1072                "Clear action to execute");
1073   RNA_def_boolean(ot->srna,
1074                   "clear_active",
1075                   0,
1076                   "Clear Active",
1077                   "Clear active track only instead of all selected tracks");
1078 }
1079
1080 /********************** disable markers operator *********************/
1081
1082 enum {
1083   MARKER_OP_DISABLE = 0,
1084   MARKER_OP_ENABLE = 1,
1085   MARKER_OP_TOGGLE = 2,
1086 };
1087
1088 static int disable_markers_exec(bContext *C, wmOperator *op)
1089 {
1090   SpaceClip *sc = CTX_wm_space_clip(C);
1091   MovieClip *clip = ED_space_clip_get_clip(sc);
1092   MovieTracking *tracking = &clip->tracking;
1093   ListBase *tracksbase = BKE_tracking_get_active_tracks(tracking);
1094   int action = RNA_enum_get(op->ptr, "action");
1095   int framenr = ED_space_clip_get_clip_frame_number(sc);
1096
1097   for (MovieTrackingTrack *track = tracksbase->first; track != NULL; track = track->next) {
1098     if (TRACK_VIEW_SELECTED(sc, track) && (track->flag & TRACK_LOCKED) == 0) {
1099       MovieTrackingMarker *marker = BKE_tracking_marker_ensure(track, framenr);
1100       switch (action) {
1101         case MARKER_OP_DISABLE:
1102           marker->flag |= MARKER_DISABLED;
1103           break;
1104         case MARKER_OP_ENABLE:
1105           marker->flag &= ~MARKER_DISABLED;
1106           break;
1107         case MARKER_OP_TOGGLE:
1108           marker->flag ^= MARKER_DISABLED;
1109           break;
1110       }
1111     }
1112   }
1113
1114   DEG_id_tag_update(&clip->id, 0);
1115
1116   WM_event_add_notifier(C, NC_MOVIECLIP | NA_EVALUATED, clip);
1117
1118   return OPERATOR_FINISHED;
1119 }
1120
1121 void CLIP_OT_disable_markers(wmOperatorType *ot)
1122 {
1123   static const EnumPropertyItem actions_items[] = {
1124       {MARKER_OP_DISABLE, "DISABLE", 0, "Disable", "Disable selected markers"},
1125       {MARKER_OP_ENABLE, "ENABLE", 0, "Enable", "Enable selected markers"},
1126       {MARKER_OP_TOGGLE, "TOGGLE", 0, "Toggle", "Toggle disabled flag for selected markers"},
1127       {0, NULL, 0, NULL, NULL},
1128   };
1129
1130   /* identifiers */
1131   ot->name = "Disable Markers";
1132   ot->description = "Disable/enable selected markers";
1133   ot->idname = "CLIP_OT_disable_markers";
1134
1135   /* api callbacks */
1136   ot->exec = disable_markers_exec;
1137   ot->poll = ED_space_clip_tracking_poll;
1138
1139   /* flags */
1140   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1141
1142   /* properties */
1143   RNA_def_enum(ot->srna, "action", actions_items, 0, "Action", "Disable action to execute");
1144 }
1145
1146 /********************** set principal center operator *********************/
1147
1148 static int set_center_principal_exec(bContext *C, wmOperator *UNUSED(op))
1149 {
1150   SpaceClip *sc = CTX_wm_space_clip(C);
1151   MovieClip *clip = ED_space_clip_get_clip(sc);
1152   int width, height;
1153
1154   BKE_movieclip_get_size(clip, &sc->user, &width, &height);
1155
1156   if (width == 0 || height == 0) {
1157     return OPERATOR_CANCELLED;
1158   }
1159
1160   clip->tracking.camera.principal[0] = ((float)width) / 2.0f;
1161   clip->tracking.camera.principal[1] = ((float)height) / 2.0f;
1162
1163   WM_event_add_notifier(C, NC_MOVIECLIP | NA_EDITED, clip);
1164
1165   return OPERATOR_FINISHED;
1166 }
1167
1168 void CLIP_OT_set_center_principal(wmOperatorType *ot)
1169 {
1170   /* identifiers */
1171   ot->name = "Set Principal to Center";
1172   ot->description = "Set optical center to center of footage";
1173   ot->idname = "CLIP_OT_set_center_principal";
1174
1175   /* api callbacks */
1176   ot->exec = set_center_principal_exec;
1177   ot->poll = ED_space_clip_tracking_poll;
1178
1179   /* flags */
1180   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1181 }
1182
1183 /********************** hide tracks operator *********************/
1184
1185 static int hide_tracks_exec(bContext *C, wmOperator *op)
1186 {
1187   SpaceClip *sc = CTX_wm_space_clip(C);
1188   MovieClip *clip = ED_space_clip_get_clip(sc);
1189   MovieTracking *tracking = &clip->tracking;
1190   int unselected;
1191
1192   unselected = RNA_boolean_get(op->ptr, "unselected");
1193
1194   /* Hide point tracks. */
1195   ListBase *tracksbase = BKE_tracking_get_active_tracks(tracking);
1196   MovieTrackingTrack *act_track = BKE_tracking_track_get_active(tracking);
1197   for (MovieTrackingTrack *track = tracksbase->first; track != NULL; track = track->next) {
1198     if (unselected == 0 && TRACK_VIEW_SELECTED(sc, track)) {
1199       track->flag |= TRACK_HIDDEN;
1200     }
1201     else if (unselected == 1 && !TRACK_VIEW_SELECTED(sc, track)) {
1202       track->flag |= TRACK_HIDDEN;
1203     }
1204   }
1205
1206   if (act_track != NULL && act_track->flag & TRACK_HIDDEN) {
1207     clip->tracking.act_track = NULL;
1208   }
1209
1210   /* Hide place tracks. */
1211   ListBase *plane_tracks_base = BKE_tracking_get_active_plane_tracks(tracking);
1212   MovieTrackingPlaneTrack *act_plane_track = BKE_tracking_plane_track_get_active(tracking);
1213   for (MovieTrackingPlaneTrack *plane_track = plane_tracks_base->first; plane_track != NULL;
1214        plane_track = plane_track->next) {
1215     if (unselected == 0 && plane_track->flag & SELECT) {
1216       plane_track->flag |= PLANE_TRACK_HIDDEN;
1217     }
1218     else if (unselected == 1 && (plane_track->flag & SELECT) == 0) {
1219       plane_track->flag |= PLANE_TRACK_HIDDEN;
1220     }
1221   }
1222   if (act_plane_track != NULL && act_plane_track->flag & TRACK_HIDDEN) {
1223     clip->tracking.act_plane_track = NULL;
1224   }
1225
1226   if (unselected == 0) {
1227     /* No selection on screen now, unlock view so it can be
1228      * scrolled nice again.
1229      */
1230     sc->flag &= ~SC_LOCK_SELECTION;
1231   }
1232
1233   BKE_tracking_dopesheet_tag_update(tracking);
1234   WM_event_add_notifier(C, NC_MOVIECLIP | ND_DISPLAY, NULL);
1235
1236   return OPERATOR_FINISHED;
1237 }
1238
1239 void CLIP_OT_hide_tracks(wmOperatorType *ot)
1240 {
1241   /* identifiers */
1242   ot->name = "Hide Tracks";
1243   ot->description = "Hide selected tracks";
1244   ot->idname = "CLIP_OT_hide_tracks";
1245
1246   /* api callbacks */
1247   ot->exec = hide_tracks_exec;
1248   ot->poll = ED_space_clip_tracking_poll;
1249
1250   /* flags */
1251   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1252
1253   /* properties */
1254   RNA_def_boolean(ot->srna, "unselected", 0, "Unselected", "Hide unselected tracks");
1255 }
1256
1257 /********************** hide tracks clear operator *********************/
1258
1259 static int hide_tracks_clear_exec(bContext *C, wmOperator *UNUSED(op))
1260 {
1261   SpaceClip *sc = CTX_wm_space_clip(C);
1262   MovieClip *clip = ED_space_clip_get_clip(sc);
1263   MovieTracking *tracking = &clip->tracking;
1264
1265   /* Unhide point tracks. */
1266   ListBase *tracksbase = BKE_tracking_get_active_tracks(tracking);
1267   for (MovieTrackingTrack *track = tracksbase->first; track != NULL; track = track->next) {
1268     track->flag &= ~TRACK_HIDDEN;
1269   }
1270
1271   /* Unhide plane tracks. */
1272   ListBase *plane_tracks_base = BKE_tracking_get_active_plane_tracks(tracking);
1273   for (MovieTrackingPlaneTrack *plane_track = plane_tracks_base->first; plane_track != NULL;
1274        plane_track = plane_track->next) {
1275     plane_track->flag &= ~PLANE_TRACK_HIDDEN;
1276   }
1277
1278   BKE_tracking_dopesheet_tag_update(tracking);
1279
1280   WM_event_add_notifier(C, NC_MOVIECLIP | ND_DISPLAY, NULL);
1281
1282   return OPERATOR_FINISHED;
1283 }
1284
1285 void CLIP_OT_hide_tracks_clear(wmOperatorType *ot)
1286 {
1287   /* identifiers */
1288   ot->name = "Hide Tracks Clear";
1289   ot->description = "Clear hide selected tracks";
1290   ot->idname = "CLIP_OT_hide_tracks_clear";
1291
1292   /* api callbacks */
1293   ot->exec = hide_tracks_clear_exec;
1294   ot->poll = ED_space_clip_tracking_poll;
1295
1296   /* flags */
1297   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1298 }
1299
1300 /********************** frame jump operator *********************/
1301
1302 static int frame_jump_exec(bContext *C, wmOperator *op)
1303 {
1304   Scene *scene = CTX_data_scene(C);
1305   SpaceClip *sc = CTX_wm_space_clip(C);
1306   MovieClip *clip = ED_space_clip_get_clip(sc);
1307   MovieTracking *tracking = &clip->tracking;
1308   int pos = RNA_enum_get(op->ptr, "position");
1309   int delta;
1310
1311   if (pos <= 1) { /* jump to path */
1312     MovieTrackingTrack *track = BKE_tracking_track_get_active(tracking);
1313     if (track == NULL) {
1314       return OPERATOR_CANCELLED;
1315     }
1316
1317     delta = pos == 1 ? 1 : -1;
1318     while (sc->user.framenr + delta >= SFRA && sc->user.framenr + delta <= EFRA) {
1319       int framenr = BKE_movieclip_remap_scene_to_clip_frame(clip, sc->user.framenr + delta);
1320       MovieTrackingMarker *marker = BKE_tracking_marker_get_exact(track, framenr);
1321
1322       if (marker == NULL || marker->flag & MARKER_DISABLED) {
1323         break;
1324       }
1325
1326       sc->user.framenr += delta;
1327     }
1328   }
1329   else { /* to failed frame */
1330     if (tracking->reconstruction.flag & TRACKING_RECONSTRUCTED) {
1331       int framenr = ED_space_clip_get_clip_frame_number(sc);
1332       MovieTrackingObject *object = BKE_tracking_object_get_active(tracking);
1333
1334       delta = pos == 3 ? 1 : -1;
1335       framenr += delta;
1336
1337       while (framenr + delta >= SFRA && framenr + delta <= EFRA) {
1338         MovieReconstructedCamera *cam = BKE_tracking_camera_get_reconstructed(
1339             tracking, object, framenr);
1340
1341         if (cam == NULL) {
1342           sc->user.framenr = BKE_movieclip_remap_clip_to_scene_frame(clip, framenr);
1343           break;
1344         }
1345
1346         framenr += delta;
1347       }
1348     }
1349   }
1350
1351   if (CFRA != sc->user.framenr) {
1352     CFRA = sc->user.framenr;
1353     BKE_sound_seek_scene(CTX_data_main(C), scene);
1354
1355     WM_event_add_notifier(C, NC_SCENE | ND_FRAME, scene);
1356   }
1357
1358   WM_event_add_notifier(C, NC_MOVIECLIP | ND_DISPLAY, NULL);
1359
1360   return OPERATOR_FINISHED;
1361 }
1362
1363 void CLIP_OT_frame_jump(wmOperatorType *ot)
1364 {
1365   static const EnumPropertyItem position_items[] = {
1366       {0, "PATHSTART", 0, "Path Start", "Jump to start of current path"},
1367       {1, "PATHEND", 0, "Path End", "Jump to end of current path"},
1368       {2, "FAILEDPREV", 0, "Previous Failed", "Jump to previous failed frame"},
1369       {2, "FAILNEXT", 0, "Next Failed", "Jump to next failed frame"},
1370       {0, NULL, 0, NULL, NULL},
1371   };
1372
1373   /* identifiers */
1374   ot->name = "Jump to Frame";
1375   ot->description = "Jump to special frame";
1376   ot->idname = "CLIP_OT_frame_jump";
1377
1378   /* api callbacks */
1379   ot->exec = frame_jump_exec;
1380   ot->poll = ED_space_clip_poll;
1381
1382   /* flags */
1383   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1384
1385   /* properties */
1386   RNA_def_enum(ot->srna, "position", position_items, 0, "Position", "Position to jump to");
1387 }
1388
1389 /********************** join tracks operator *********************/
1390
1391 static int join_tracks_exec(bContext *C, wmOperator *op)
1392 {
1393   SpaceClip *sc = CTX_wm_space_clip(C);
1394   MovieClip *clip = ED_space_clip_get_clip(sc);
1395   MovieTracking *tracking = &clip->tracking;
1396   MovieTrackingStabilization *stab = &tracking->stabilization;
1397   ListBase *tracksbase = BKE_tracking_get_active_tracks(tracking);
1398   ListBase *plane_tracks_base = BKE_tracking_get_active_plane_tracks(tracking);
1399   bool update_stabilization = false;
1400
1401   MovieTrackingTrack *act_track = BKE_tracking_track_get_active(tracking);
1402   if (act_track == NULL) {
1403     BKE_report(op->reports, RPT_ERROR, "No active track to join to");
1404     return OPERATOR_CANCELLED;
1405   }
1406
1407   GSet *point_tracks = BLI_gset_ptr_new(__func__);
1408
1409   for (MovieTrackingTrack *track = tracksbase->first, *next_track; track != NULL;
1410        track = next_track) {
1411     next_track = track->next;
1412     if (TRACK_VIEW_SELECTED(sc, track) && track != act_track) {
1413       BKE_tracking_tracks_join(tracking, act_track, track);
1414
1415       if (track->flag & TRACK_USE_2D_STAB) {
1416         update_stabilization = true;
1417         if ((act_track->flag & TRACK_USE_2D_STAB) == 0) {
1418           act_track->flag |= TRACK_USE_2D_STAB;
1419         }
1420         else {
1421           stab->tot_track--;
1422         }
1423         BLI_assert(0 <= stab->tot_track);
1424       }
1425       if (track->flag & TRACK_USE_2D_STAB_ROT) {
1426         update_stabilization = true;
1427         if ((act_track->flag & TRACK_USE_2D_STAB_ROT) == 0) {
1428           act_track->flag |= TRACK_USE_2D_STAB_ROT;
1429         }
1430         else {
1431           stab->tot_rot_track--;
1432         }
1433         BLI_assert(0 <= stab->tot_rot_track);
1434       }
1435
1436       for (MovieTrackingPlaneTrack *plane_track = plane_tracks_base->first; plane_track != NULL;
1437            plane_track = plane_track->next) {
1438         if (BKE_tracking_plane_track_has_point_track(plane_track, track)) {
1439           BKE_tracking_plane_track_replace_point_track(plane_track, track, act_track);
1440           if ((plane_track->flag & PLANE_TRACK_AUTOKEY) == 0) {
1441             BLI_gset_insert(point_tracks, plane_track);
1442           }
1443         }
1444       }
1445
1446       BKE_tracking_track_free(track);
1447       BLI_freelinkN(tracksbase, track);
1448     }
1449   }
1450
1451   if (update_stabilization) {
1452     WM_event_add_notifier(C, NC_MOVIECLIP | ND_DISPLAY, clip);
1453   }
1454
1455   GSetIterator gs_iter;
1456   int framenr = ED_space_clip_get_clip_frame_number(sc);
1457   GSET_ITER (gs_iter, point_tracks) {
1458     MovieTrackingPlaneTrack *plane_track = BLI_gsetIterator_getKey(&gs_iter);
1459     BKE_tracking_track_plane_from_existing_motion(plane_track, framenr);
1460   }
1461
1462   BLI_gset_free(point_tracks, NULL);
1463
1464   WM_event_add_notifier(C, NC_MOVIECLIP | NA_EDITED, clip);
1465
1466   return OPERATOR_FINISHED;
1467 }
1468
1469 void CLIP_OT_join_tracks(wmOperatorType *ot)
1470 {
1471   /* identifiers */
1472   ot->name = "Join Tracks";
1473   ot->description = "Join selected tracks";
1474   ot->idname = "CLIP_OT_join_tracks";
1475
1476   /* api callbacks */
1477   ot->exec = join_tracks_exec;
1478   ot->poll = ED_space_clip_tracking_poll;
1479
1480   /* flags */
1481   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1482 }
1483
1484 /********************** lock tracks operator *********************/
1485
1486 enum {
1487   TRACK_ACTION_LOCK = 0,
1488   TRACK_ACTION_UNLOCK = 1,
1489   TRACK_ACTION_TOGGLE = 2,
1490 };
1491
1492 static int lock_tracks_exec(bContext *C, wmOperator *op)
1493 {
1494   SpaceClip *sc = CTX_wm_space_clip(C);
1495   MovieClip *clip = ED_space_clip_get_clip(sc);
1496   MovieTracking *tracking = &clip->tracking;
1497   ListBase *tracksbase = BKE_tracking_get_active_tracks(tracking);
1498   int action = RNA_enum_get(op->ptr, "action");
1499
1500   for (MovieTrackingTrack *track = tracksbase->first; track != NULL; track = track->next) {
1501     if (TRACK_VIEW_SELECTED(sc, track)) {
1502       switch (action) {
1503         case TRACK_ACTION_LOCK:
1504           track->flag |= TRACK_LOCKED;
1505           break;
1506         case TRACK_ACTION_UNLOCK:
1507           track->flag &= ~TRACK_LOCKED;
1508           break;
1509         case TRACK_ACTION_TOGGLE:
1510           track->flag ^= TRACK_LOCKED;
1511           break;
1512       }
1513     }
1514   }
1515
1516   WM_event_add_notifier(C, NC_MOVIECLIP | NA_EVALUATED, clip);
1517
1518   return OPERATOR_FINISHED;
1519 }
1520
1521 void CLIP_OT_lock_tracks(wmOperatorType *ot)
1522 {
1523   static const EnumPropertyItem actions_items[] = {
1524       {TRACK_ACTION_LOCK, "LOCK", 0, "Lock", "Lock selected tracks"},
1525       {TRACK_ACTION_UNLOCK, "UNLOCK", 0, "Unlock", "Unlock selected tracks"},
1526       {TRACK_ACTION_TOGGLE, "TOGGLE", 0, "Toggle", "Toggle locked flag for selected tracks"},
1527       {0, NULL, 0, NULL, NULL},
1528   };
1529
1530   /* identifiers */
1531   ot->name = "Lock Tracks";
1532   ot->description = "Lock/unlock selected tracks";
1533   ot->idname = "CLIP_OT_lock_tracks";
1534
1535   /* api callbacks */
1536   ot->exec = lock_tracks_exec;
1537   ot->poll = ED_space_clip_tracking_poll;
1538
1539   /* flags */
1540   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1541
1542   /* properties */
1543   RNA_def_enum(ot->srna, "action", actions_items, 0, "Action", "Lock action to execute");
1544 }
1545
1546 /********************** set keyframe operator *********************/
1547
1548 enum {
1549   SOLVER_KEYFRAME_A = 0,
1550   SOLVER_KEYFRAME_B = 1,
1551 };
1552
1553 static int set_solver_keyframe_exec(bContext *C, wmOperator *op)
1554 {
1555   SpaceClip *sc = CTX_wm_space_clip(C);
1556   MovieClip *clip = ED_space_clip_get_clip(sc);
1557   MovieTracking *tracking = &clip->tracking;
1558   MovieTrackingObject *object = BKE_tracking_object_get_active(tracking);
1559   int keyframe = RNA_enum_get(op->ptr, "keyframe");
1560   int framenr = BKE_movieclip_remap_scene_to_clip_frame(clip, sc->user.framenr);
1561
1562   if (keyframe == SOLVER_KEYFRAME_A) {
1563     object->keyframe1 = framenr;
1564   }
1565   else {
1566     object->keyframe2 = framenr;
1567   }
1568
1569   WM_event_add_notifier(C, NC_MOVIECLIP | ND_DISPLAY, clip);
1570
1571   return OPERATOR_FINISHED;
1572 }
1573
1574 void CLIP_OT_set_solver_keyframe(wmOperatorType *ot)
1575 {
1576   static const EnumPropertyItem keyframe_items[] = {
1577       {SOLVER_KEYFRAME_A, "KEYFRAME_A", 0, "Keyframe A", ""},
1578       {SOLVER_KEYFRAME_B, "KEYFRAME_B", 0, "Keyframe B", ""},
1579       {0, NULL, 0, NULL, NULL},
1580   };
1581
1582   /* identifiers */
1583   ot->name = "Set Solver Keyframe";
1584   ot->description = "Set keyframe used by solver";
1585   ot->idname = "CLIP_OT_set_solver_keyframe";
1586
1587   /* api callbacks */
1588   ot->exec = set_solver_keyframe_exec;
1589   ot->poll = ED_space_clip_tracking_poll;
1590
1591   /* flags */
1592   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1593
1594   /* properties */
1595   RNA_def_enum(ot->srna, "keyframe", keyframe_items, 0, "Keyframe", "Keyframe to set");
1596 }
1597
1598 /********************** track copy color operator *********************/
1599
1600 static int track_copy_color_exec(bContext *C, wmOperator *UNUSED(op))
1601 {
1602   SpaceClip *sc = CTX_wm_space_clip(C);
1603   MovieClip *clip = ED_space_clip_get_clip(sc);
1604   MovieTracking *tracking = &clip->tracking;
1605   ListBase *tracksbase = BKE_tracking_get_active_tracks(tracking);
1606
1607   MovieTrackingTrack *act_track = BKE_tracking_track_get_active(tracking);
1608   if (act_track == NULL) {
1609     return OPERATOR_CANCELLED;
1610   }
1611
1612   for (MovieTrackingTrack *track = tracksbase->first; track != NULL; track = track->next) {
1613     if (TRACK_VIEW_SELECTED(sc, track) && track != act_track) {
1614       track->flag &= ~TRACK_CUSTOMCOLOR;
1615       if (act_track->flag & TRACK_CUSTOMCOLOR) {
1616         copy_v3_v3(track->color, act_track->color);
1617         track->flag |= TRACK_CUSTOMCOLOR;
1618       }
1619     }
1620   }
1621
1622   WM_event_add_notifier(C, NC_MOVIECLIP | ND_DISPLAY, clip);
1623
1624   return OPERATOR_FINISHED;
1625 }
1626
1627 void CLIP_OT_track_copy_color(wmOperatorType *ot)
1628 {
1629   /* identifiers */
1630   ot->name = "Copy Color";
1631   ot->description = "Copy color to all selected tracks";
1632   ot->idname = "CLIP_OT_track_copy_color";
1633
1634   /* api callbacks */
1635   ot->exec = track_copy_color_exec;
1636   ot->poll = ED_space_clip_tracking_poll;
1637
1638   /* flags */
1639   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1640 }
1641
1642 /********************** clean tracks operator *********************/
1643
1644 static bool is_track_clean(MovieTrackingTrack *track, int frames, int del)
1645 {
1646   bool ok = true;
1647   int prev = -1, count = 0;
1648   MovieTrackingMarker *markers = track->markers, *new_markers = NULL;
1649   int start_disabled = 0;
1650   int markersnr = track->markersnr;
1651
1652   if (del) {
1653     new_markers = MEM_callocN(markersnr * sizeof(MovieTrackingMarker), "track cleaned markers");
1654   }
1655
1656   for (int a = 0; a < markersnr; a++) {
1657     int end = 0;
1658
1659     if (prev == -1) {
1660       if ((markers[a].flag & MARKER_DISABLED) == 0) {
1661         prev = a;
1662       }
1663       else {
1664         start_disabled = 1;
1665       }
1666     }
1667
1668     if (prev >= 0) {
1669       end = a == markersnr - 1;
1670       end |= (a < markersnr - 1) && (markers[a].framenr != markers[a + 1].framenr - 1 ||
1671                                      markers[a].flag & MARKER_DISABLED);
1672     }
1673
1674     if (end) {
1675       int segok = 1, len = 0;
1676
1677       if (a != prev && markers[a].framenr != markers[a - 1].framenr + 1) {
1678         len = a - prev;
1679       }
1680       else if (markers[a].flag & MARKER_DISABLED) {
1681         len = a - prev;
1682       }
1683       else {
1684         len = a - prev + 1;
1685       }
1686
1687       if (frames) {
1688         if (len < frames) {
1689           segok = 0;
1690           ok = 0;
1691
1692           if (!del) {
1693             break;
1694           }
1695         }
1696       }
1697
1698       if (del) {
1699         if (segok) {
1700           int t = len;
1701
1702           if (markers[a].flag & MARKER_DISABLED) {
1703             t++;
1704           }
1705
1706           /* Place disabled marker in front of current segment. */
1707           if (start_disabled) {
1708             memcpy(new_markers + count, markers + prev, sizeof(MovieTrackingMarker));
1709             new_markers[count].framenr--;
1710             new_markers[count].flag |= MARKER_DISABLED;
1711
1712             count++;
1713             start_disabled = 0;
1714           }
1715
1716           memcpy(new_markers + count, markers + prev, t * sizeof(MovieTrackingMarker));
1717           count += t;
1718         }
1719         else if (markers[a].flag & MARKER_DISABLED) {
1720           /* Current segment which would be deleted was finished by
1721            * disabled marker, so next segment should be started from
1722            * disabled marker.
1723            */
1724           start_disabled = 1;
1725         }
1726       }
1727
1728       prev = -1;
1729     }
1730   }
1731
1732   if (del && count == 0) {
1733     ok = 0;
1734   }
1735
1736   if (del) {
1737     MEM_freeN(track->markers);
1738
1739     if (count) {
1740       track->markers = new_markers;
1741     }
1742     else {
1743       track->markers = NULL;
1744       MEM_freeN(new_markers);
1745     }
1746
1747     track->markersnr = count;
1748   }
1749
1750   return ok;
1751 }
1752
1753 static int clean_tracks_exec(bContext *C, wmOperator *op)
1754 {
1755   SpaceClip *sc = CTX_wm_space_clip(C);
1756   MovieClip *clip = ED_space_clip_get_clip(sc);
1757   MovieTracking *tracking = &clip->tracking;
1758   ListBase *tracksbase = BKE_tracking_get_active_tracks(tracking);
1759   MovieTrackingTrack *act_track = BKE_tracking_track_get_active(tracking);
1760   int frames = RNA_int_get(op->ptr, "frames");
1761   int action = RNA_enum_get(op->ptr, "action");
1762   float error = RNA_float_get(op->ptr, "error");
1763
1764   if (error && action == TRACKING_CLEAN_DELETE_SEGMENT) {
1765     action = TRACKING_CLEAN_DELETE_TRACK;
1766   }
1767
1768   for (MovieTrackingTrack *track = tracksbase->first, *next_track; track != NULL;
1769        track = next_track) {
1770     next_track = track->next;
1771
1772     if ((track->flag & TRACK_HIDDEN) == 0 && (track->flag & TRACK_LOCKED) == 0) {
1773       bool ok;
1774
1775       ok = (is_track_clean(track, frames, action == TRACKING_CLEAN_DELETE_SEGMENT)) &&
1776            ((error == 0.0f) || (track->flag & TRACK_HAS_BUNDLE) == 0 || (track->error < error));
1777
1778       if (!ok) {
1779         if (action == TRACKING_CLEAN_SELECT) {
1780           BKE_tracking_track_flag_set(track, TRACK_AREA_ALL, SELECT);
1781         }
1782         else if (action == TRACKING_CLEAN_DELETE_TRACK) {
1783           if (track == act_track) {
1784             clip->tracking.act_track = NULL;
1785           }
1786           BKE_tracking_track_free(track);
1787           BLI_freelinkN(tracksbase, track);
1788           track = NULL;
1789         }
1790
1791         /* Happens when all tracking segments are not long enough. */
1792         if (track && track->markersnr == 0) {
1793           if (track == act_track) {
1794             clip->tracking.act_track = NULL;
1795           }
1796           BKE_tracking_track_free(track);
1797           BLI_freelinkN(tracksbase, track);
1798         }
1799       }
1800     }
1801   }
1802
1803   BKE_tracking_dopesheet_tag_update(tracking);
1804
1805   WM_event_add_notifier(C, NC_MOVIECLIP | ND_SELECT, clip);
1806
1807   return OPERATOR_FINISHED;
1808 }
1809
1810 static int clean_tracks_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
1811 {
1812   SpaceClip *sc = CTX_wm_space_clip(C);
1813   MovieClip *clip = ED_space_clip_get_clip(sc);
1814
1815   if (!RNA_struct_property_is_set(op->ptr, "frames")) {
1816     RNA_int_set(op->ptr, "frames", clip->tracking.settings.clean_frames);
1817   }
1818
1819   if (!RNA_struct_property_is_set(op->ptr, "error")) {
1820     RNA_float_set(op->ptr, "error", clip->tracking.settings.clean_error);
1821   }
1822
1823   if (!RNA_struct_property_is_set(op->ptr, "action")) {
1824     RNA_enum_set(op->ptr, "action", clip->tracking.settings.clean_action);
1825   }
1826
1827   return clean_tracks_exec(C, op);
1828 }
1829
1830 void CLIP_OT_clean_tracks(wmOperatorType *ot)
1831 {
1832   static const EnumPropertyItem actions_items[] = {
1833       {TRACKING_CLEAN_SELECT, "SELECT", 0, "Select", "Select unclean tracks"},
1834       {TRACKING_CLEAN_DELETE_TRACK, "DELETE_TRACK", 0, "Delete Track", "Delete unclean tracks"},
1835       {TRACKING_CLEAN_DELETE_SEGMENT,
1836        "DELETE_SEGMENTS",
1837        0,
1838        "Delete Segments",
1839        "Delete unclean segments of tracks"},
1840       {0, NULL, 0, NULL, NULL},
1841   };
1842
1843   /* identifiers */
1844   ot->name = "Clean Tracks";
1845   ot->description = "Clean tracks with high error values or few frames";
1846   ot->idname = "CLIP_OT_clean_tracks";
1847
1848   /* api callbacks */
1849   ot->exec = clean_tracks_exec;
1850   ot->invoke = clean_tracks_invoke;
1851   ot->poll = ED_space_clip_tracking_poll;
1852
1853   /* flags */
1854   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1855
1856   /* properties */
1857   RNA_def_int(ot->srna,
1858               "frames",
1859               0,
1860               0,
1861               INT_MAX,
1862               "Tracked Frames",
1863               "Effect on tracks which are tracked less than "
1864               "specified amount of frames",
1865               0,
1866               INT_MAX);
1867   RNA_def_float(ot->srna,
1868                 "error",
1869                 0.0f,
1870                 0.0f,
1871                 FLT_MAX,
1872                 "Reprojection Error",
1873                 "Effect on tracks which have got larger re-projection error",
1874                 0.0f,
1875                 100.0f);
1876   RNA_def_enum(ot->srna, "action", actions_items, 0, "Action", "Cleanup action to execute");
1877 }
1878
1879 /********************** add tracking object *********************/
1880
1881 static int tracking_object_new_exec(bContext *C, wmOperator *UNUSED(op))
1882 {
1883   SpaceClip *sc = CTX_wm_space_clip(C);
1884   MovieClip *clip = ED_space_clip_get_clip(sc);
1885   MovieTracking *tracking = &clip->tracking;
1886
1887   BKE_tracking_object_add(tracking, "Object");
1888
1889   WM_event_add_notifier(C, NC_MOVIECLIP | NA_EDITED, clip);
1890
1891   return OPERATOR_FINISHED;
1892 }
1893
1894 void CLIP_OT_tracking_object_new(wmOperatorType *ot)
1895 {
1896   /* identifiers */
1897   ot->name = "Add Tracking Object";
1898   ot->description = "Add new object for tracking";
1899   ot->idname = "CLIP_OT_tracking_object_new";
1900
1901   /* api callbacks */
1902   ot->exec = tracking_object_new_exec;
1903   ot->poll = ED_space_clip_tracking_poll;
1904
1905   /* flags */
1906   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1907 }
1908
1909 /********************** remove tracking object *********************/
1910
1911 static int tracking_object_remove_exec(bContext *C, wmOperator *op)
1912 {
1913   SpaceClip *sc = CTX_wm_space_clip(C);
1914   MovieClip *clip = ED_space_clip_get_clip(sc);
1915   MovieTracking *tracking = &clip->tracking;
1916   MovieTrackingObject *object;
1917
1918   object = BKE_tracking_object_get_active(tracking);
1919
1920   if (object->flag & TRACKING_OBJECT_CAMERA) {
1921     BKE_report(op->reports, RPT_WARNING, "Object used for camera tracking cannot be deleted");
1922     return OPERATOR_CANCELLED;
1923   }
1924
1925   BKE_tracking_object_delete(tracking, object);
1926
1927   WM_event_add_notifier(C, NC_MOVIECLIP | NA_EDITED, clip);
1928
1929   return OPERATOR_FINISHED;
1930 }
1931
1932 void CLIP_OT_tracking_object_remove(wmOperatorType *ot)
1933 {
1934   /* identifiers */
1935   ot->name = "Remove Tracking Object";
1936   ot->description = "Remove object for tracking";
1937   ot->idname = "CLIP_OT_tracking_object_remove";
1938
1939   /* api callbacks */
1940   ot->exec = tracking_object_remove_exec;
1941   ot->poll = ED_space_clip_tracking_poll;
1942
1943   /* flags */
1944   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1945 }
1946
1947 /********************** copy tracks to clipboard operator *********************/
1948
1949 static int copy_tracks_exec(bContext *C, wmOperator *UNUSED(op))
1950 {
1951   SpaceClip *sc = CTX_wm_space_clip(C);
1952   MovieClip *clip = ED_space_clip_get_clip(sc);
1953   MovieTracking *tracking = &clip->tracking;
1954   MovieTrackingObject *object = BKE_tracking_object_get_active(tracking);
1955
1956   clip_tracking_clear_invisible_track_selection(sc, clip);
1957
1958   BKE_tracking_clipboard_copy_tracks(tracking, object);
1959
1960   return OPERATOR_FINISHED;
1961 }
1962
1963 void CLIP_OT_copy_tracks(wmOperatorType *ot)
1964 {
1965   /* identifiers */
1966   ot->name = "Copy Tracks";
1967   ot->description = "Copy selected tracks to clipboard";
1968   ot->idname = "CLIP_OT_copy_tracks";
1969
1970   /* api callbacks */
1971   ot->exec = copy_tracks_exec;
1972   ot->poll = ED_space_clip_tracking_poll;
1973
1974   /* flags */
1975   ot->flag = OPTYPE_REGISTER;
1976 }
1977
1978 /********************* paste tracks from clipboard operator ********************/
1979
1980 static bool paste_tracks_poll(bContext *C)
1981 {
1982   if (ED_space_clip_tracking_poll(C)) {
1983     return BKE_tracking_clipboard_has_tracks();
1984   }
1985
1986   return 0;
1987 }
1988
1989 static int paste_tracks_exec(bContext *C, wmOperator *UNUSED(op))
1990 {
1991   SpaceClip *sc = CTX_wm_space_clip(C);
1992   MovieClip *clip = ED_space_clip_get_clip(sc);
1993   MovieTracking *tracking = &clip->tracking;
1994   MovieTrackingObject *object = BKE_tracking_object_get_active(tracking);
1995   ListBase *tracks_base = BKE_tracking_object_get_tracks(tracking, object);
1996
1997   BKE_tracking_tracks_deselect_all(tracks_base);
1998   BKE_tracking_clipboard_paste_tracks(tracking, object);
1999
2000   WM_event_add_notifier(C, NC_MOVIECLIP | NA_EDITED, clip);
2001
2002   return OPERATOR_FINISHED;
2003 }
2004
2005 void CLIP_OT_paste_tracks(wmOperatorType *ot)
2006 {
2007   /* identifiers */
2008   ot->name = "Paste Tracks";
2009   ot->description = "Paste tracks from clipboard";
2010   ot->idname = "CLIP_OT_paste_tracks";
2011
2012   /* api callbacks */
2013   ot->exec = paste_tracks_exec;
2014   ot->poll = paste_tracks_poll;
2015
2016   /* flags */
2017   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2018 }
2019
2020 /********************** Insert track keyframe operator *********************/
2021
2022 static void keyframe_set_flag(bContext *C, bool set)
2023 {
2024   SpaceClip *sc = CTX_wm_space_clip(C);
2025   MovieClip *clip = ED_space_clip_get_clip(sc);
2026   MovieTracking *tracking = &clip->tracking;
2027   int framenr = ED_space_clip_get_clip_frame_number(sc);
2028
2029   ListBase *tracks_base = BKE_tracking_get_active_tracks(tracking);
2030   for (MovieTrackingTrack *track = tracks_base->first; track != NULL; track = track->next) {
2031     if (TRACK_VIEW_SELECTED(sc, track)) {
2032       if (set) {
2033         MovieTrackingMarker *marker = BKE_tracking_marker_ensure(track, framenr);
2034         marker->flag &= ~MARKER_TRACKED;
2035       }
2036       else {
2037         MovieTrackingMarker *marker = BKE_tracking_marker_get_exact(track, framenr);
2038         if (marker != NULL) {
2039           marker->flag |= MARKER_TRACKED;
2040         }
2041       }
2042     }
2043   }
2044
2045   ListBase *plane_tracks_base = BKE_tracking_get_active_plane_tracks(tracking);
2046   for (MovieTrackingPlaneTrack *plane_track = plane_tracks_base->first; plane_track != NULL;
2047        plane_track = plane_track->next) {
2048     if (PLANE_TRACK_VIEW_SELECTED(plane_track)) {
2049       if (set) {
2050         MovieTrackingPlaneMarker *plane_marker = BKE_tracking_plane_marker_ensure(plane_track,
2051                                                                                   framenr);
2052         if (plane_marker->flag & PLANE_MARKER_TRACKED) {
2053           plane_marker->flag &= ~PLANE_MARKER_TRACKED;
2054           BKE_tracking_track_plane_from_existing_motion(plane_track, plane_marker->framenr);
2055         }
2056       }
2057       else {
2058         MovieTrackingPlaneMarker *plane_marker = BKE_tracking_plane_marker_get_exact(plane_track,
2059                                                                                      framenr);
2060         if (plane_marker) {
2061           if ((plane_marker->flag & PLANE_MARKER_TRACKED) == 0) {
2062             plane_marker->flag |= PLANE_MARKER_TRACKED;
2063             BKE_tracking_retrack_plane_from_existing_motion_at_segment(plane_track,
2064                                                                        plane_marker->framenr);
2065           }
2066         }
2067       }
2068     }
2069   }
2070
2071   WM_event_add_notifier(C, NC_MOVIECLIP | NA_EDITED, clip);
2072 }
2073
2074 static int keyframe_insert_exec(bContext *C, wmOperator *UNUSED(op))
2075 {
2076   keyframe_set_flag(C, true);
2077   return OPERATOR_FINISHED;
2078 }
2079
2080 void CLIP_OT_keyframe_insert(wmOperatorType *ot)
2081 {
2082   /* identifiers */
2083   ot->name = "Insert keyframe";
2084   ot->description = "Insert a keyframe to selected tracks at current frame";
2085   ot->idname = "CLIP_OT_keyframe_insert";
2086
2087   /* api callbacks */
2088   ot->poll = ED_space_clip_tracking_poll;
2089   ot->exec = keyframe_insert_exec;
2090
2091   /* flags */
2092   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2093 }
2094
2095 /********************** Delete track keyframe operator *********************/
2096
2097 static int keyframe_delete_exec(bContext *C, wmOperator *UNUSED(op))
2098 {
2099   keyframe_set_flag(C, false);
2100   return OPERATOR_FINISHED;
2101 }
2102
2103 void CLIP_OT_keyframe_delete(wmOperatorType *ot)
2104 {
2105   /* identifiers */
2106   ot->name = "Delete keyframe";
2107   ot->description = "Delete a keyframe from selected tracks at current frame";
2108   ot->idname = "CLIP_OT_keyframe_delete";
2109
2110   /* api callbacks */
2111   ot->poll = ED_space_clip_tracking_poll;
2112   ot->exec = keyframe_delete_exec;
2113
2114   /* flags */
2115   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2116 }