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