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