Internal refactoring of tracking module, should be no functional changes
[blender.git] / source / blender / editors / space_clip / clip_ops.c
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * The Original Code is Copyright (C) 2011 Blender Foundation.
19  * All rights reserved.
20  *
21  *
22  * Contributor(s): Blender Foundation,
23  *                 Sergey Sharybin
24  *
25  * ***** END GPL LICENSE BLOCK *****
26  */
27
28 /** \file blender/editors/space_clip/clip_ops.c
29  *  \ingroup spclip
30  */
31
32 #include <errno.h>
33
34 #include "MEM_guardedalloc.h"
35
36 #include "DNA_userdef_types.h"
37 #include "DNA_scene_types.h"    /* min/max frames */
38
39 #include "BLI_path_util.h"
40 #include "BLI_utildefines.h"
41 #include "BLI_math.h"
42
43 #include "BKE_context.h"
44 #include "BKE_global.h"
45 #include "BKE_report.h"
46 #include "BKE_main.h"
47 #include "BKE_library.h"
48 #include "BKE_movieclip.h"
49 #include "BKE_sound.h"
50 #include "BKE_tracking.h"
51
52 #include "WM_api.h"
53 #include "WM_types.h"
54
55 #include "IMB_imbuf_types.h"
56 #include "IMB_imbuf.h"
57
58 #include "ED_screen.h"
59 #include "ED_clip.h"
60
61 #include "UI_interface.h"
62
63 #include "RNA_access.h"
64 #include "RNA_define.h"
65
66 #include "UI_view2d.h"
67
68 #include "clip_intern.h"        // own include
69
70 /******************** view navigation utilities *********************/
71
72 static void sclip_zoom_set(SpaceClip *sc, ARegion *ar, float zoom, float location[2])
73 {
74         float oldzoom = sc->zoom;
75         int width, height;
76
77         sc->zoom = zoom;
78
79         if (sc->zoom < 0.1f || sc->zoom > 4.0f) {
80                 /* check zoom limits */
81                 ED_space_clip_size(sc, &width, &height);
82
83                 width *= sc->zoom;
84                 height *= sc->zoom;
85
86                 if ((width < 4) && (height < 4))
87                         sc->zoom = oldzoom;
88                 else if ((ar->winrct.xmax - ar->winrct.xmin) <= sc->zoom)
89                         sc->zoom = oldzoom;
90                 else if ((ar->winrct.ymax - ar->winrct.ymin) <= sc->zoom)
91                         sc->zoom = oldzoom;
92         }
93
94         if ((U.uiflag & USER_ZOOM_TO_MOUSEPOS) && location) {
95                 ED_space_clip_size(sc, &width, &height);
96
97                 sc->xof += ((location[0] - 0.5f) * width - sc->xof) * (sc->zoom - oldzoom) / sc->zoom;
98                 sc->yof += ((location[1] - 0.5f) * height - sc->yof) * (sc->zoom - oldzoom) / sc->zoom;
99         }
100 }
101
102 static void sclip_zoom_set_factor(SpaceClip *sc, ARegion *ar, float zoomfac, float location[2])
103 {
104         sclip_zoom_set(sc, ar, sc->zoom*zoomfac, location);
105 }
106
107 static void sclip_zoom_set_factor_exec(bContext *C, wmEvent *event, float factor)
108 {
109         SpaceClip *sc = CTX_wm_space_clip(C);
110         ARegion *ar = CTX_wm_region(C);
111         float location[2], *mpos = NULL;
112
113         if (event) {
114                 ED_clip_mouse_pos(C, event, location);
115                 mpos = location;
116         }
117
118         sclip_zoom_set_factor(sc, ar, factor, mpos);
119
120         ED_region_tag_redraw(CTX_wm_region(C));
121 }
122
123 /******************** open clip operator ********************/
124
125 static void clip_filesel(bContext *C, wmOperator *op, const char *path)
126 {
127         RNA_string_set(op->ptr, "directory", path);
128
129         WM_event_add_fileselect(C, op);
130 }
131
132 static void open_init(bContext *C, wmOperator *op)
133 {
134         PropertyPointerRNA *pprop;
135
136         op->customdata = pprop = MEM_callocN(sizeof(PropertyPointerRNA), "OpenPropertyPointerRNA");
137         uiIDContextProperty(C, &pprop->ptr, &pprop->prop);
138 }
139
140 static int open_cancel(bContext *UNUSED(C), wmOperator *op)
141 {
142         MEM_freeN(op->customdata);
143         op->customdata = NULL;
144
145         return OPERATOR_CANCELLED;
146 }
147
148 static int open_exec(bContext *C, wmOperator *op)
149 {
150         SpaceClip *sc = CTX_wm_space_clip(C);
151         bScreen *screen = CTX_wm_screen(C);
152         PropertyPointerRNA *pprop;
153         PointerRNA idptr;
154         MovieClip *clip = NULL;
155         char str[FILE_MAX];
156
157         if (RNA_collection_length(op->ptr, "files")) {
158                 PointerRNA fileptr;
159                 PropertyRNA *prop;
160                 char dir_only[FILE_MAX], file_only[FILE_MAX];
161                 int relative = RNA_boolean_get(op->ptr, "relative_path");
162
163                 RNA_string_get(op->ptr, "directory", dir_only);
164                 if (relative)
165                         BLI_path_rel(dir_only, G.main->name);
166
167                 prop = RNA_struct_find_property(op->ptr, "files");
168                 RNA_property_collection_lookup_int(op->ptr, prop, 0, &fileptr);
169                 RNA_string_get(&fileptr, "name", file_only);
170
171                 BLI_join_dirfile(str, sizeof(str), dir_only, file_only);
172         }
173         else {
174                 BKE_reportf(op->reports, RPT_ERROR, "No files selected to be opened");
175
176                 return OPERATOR_CANCELLED;
177         }
178
179         /* default to frame 1 if there's no scene in context */
180
181         errno = 0;
182
183         clip = BKE_movieclip_file_add(str);
184
185         if (!clip) {
186                 if (op->customdata)
187                         MEM_freeN(op->customdata);
188
189                 BKE_reportf(op->reports, RPT_ERROR, "Can't read: \"%s\", %s.", str,
190                             errno ? strerror(errno) : "Unsupported movie clip format");
191
192                 return OPERATOR_CANCELLED;
193         }
194
195         if (!op->customdata)
196                 open_init(C, op);
197
198         /* hook into UI */
199         pprop = op->customdata;
200
201         if (pprop->prop) {
202                 /* when creating new ID blocks, use is already 1, but RNA
203                  * pointer se also increases user, so this compensates it */
204                 clip->id.us--;
205
206                 RNA_id_pointer_create(&clip->id, &idptr);
207                 RNA_property_pointer_set(&pprop->ptr, pprop->prop, idptr);
208                 RNA_property_update(C, &pprop->ptr, pprop->prop);
209         }
210         else if (sc) {
211                 ED_space_clip_set(C, screen, sc, clip);
212         }
213
214         WM_event_add_notifier(C, NC_MOVIECLIP | NA_ADDED, clip);
215
216         MEM_freeN(op->customdata);
217
218         return OPERATOR_FINISHED;
219 }
220
221 static int open_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(event))
222 {
223         SpaceClip *sc = CTX_wm_space_clip(C);
224         char path[FILE_MAX];
225         MovieClip *clip = NULL;
226
227         if (sc)
228                 clip = ED_space_clip(sc);
229
230         if (clip) {
231                 strncpy(path, clip->name, sizeof(path));
232
233                 BLI_path_abs(path, G.main->name);
234                 BLI_parent_dir(path);
235         }
236         else {
237                 strncpy(path, U.textudir, sizeof(path));
238         }
239
240         if (!RNA_struct_property_is_set(op->ptr, "relative_path"))
241                 RNA_boolean_set(op->ptr, "relative_path", U.flag & USER_RELPATHS);
242
243         if (RNA_struct_property_is_set(op->ptr, "filepath"))
244                 return open_exec(C, op);
245
246         open_init(C, op);
247
248         clip_filesel(C, op, path);
249
250         return OPERATOR_RUNNING_MODAL;
251 }
252
253 void CLIP_OT_open(wmOperatorType *ot)
254 {
255         /* identifiers */
256         ot->name = "Open Clip";
257         ot->description = "Load a sequence of frames or a movie file";
258         ot->idname = "CLIP_OT_open";
259
260         /* api callbacks */
261         ot->exec = open_exec;
262         ot->invoke = open_invoke;
263         ot->cancel = open_cancel;
264
265         /* flags */
266         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
267
268         /* properties */
269         WM_operator_properties_filesel(ot, FOLDERFILE | IMAGEFILE | MOVIEFILE, FILE_SPECIAL, FILE_OPENFILE,
270                                        WM_FILESEL_RELPATH | WM_FILESEL_FILES | WM_FILESEL_DIRECTORY,
271                                        FILE_DEFAULTDISPLAY);
272 }
273
274 /******************* reload clip operator *********************/
275
276 static int reload_exec(bContext *C, wmOperator *UNUSED(op))
277 {
278         MovieClip *clip = CTX_data_edit_movieclip(C);
279
280         if (!clip)
281                 return OPERATOR_CANCELLED;
282
283         BKE_movieclip_reload(clip);
284
285         WM_event_add_notifier(C, NC_MOVIECLIP | NA_EDITED, clip);
286
287         return OPERATOR_FINISHED;
288 }
289
290 void CLIP_OT_reload(wmOperatorType *ot)
291 {
292         /* identifiers */
293         ot->name = "Reload Clip";
294         ot->description = "Reload clip";
295         ot->idname = "CLIP_OT_reload";
296
297         /* api callbacks */
298         ot->exec = reload_exec;
299 }
300
301 /********************** view pan operator *********************/
302
303 typedef struct ViewPanData {
304         float x, y;
305         float xof, yof, xorig, yorig;
306         int event_type;
307         float *vec;
308 } ViewPanData;
309
310 static void view_pan_init(bContext *C, wmOperator *op, wmEvent *event)
311 {
312         SpaceClip *sc = CTX_wm_space_clip(C);
313         ViewPanData *vpd;
314
315         op->customdata = vpd = MEM_callocN(sizeof(ViewPanData), "ClipViewPanData");
316         WM_cursor_modal(CTX_wm_window(C), BC_NSEW_SCROLLCURSOR);
317
318         vpd->x = event->x;
319         vpd->y = event->y;
320
321         if (sc->flag & SC_LOCK_SELECTION)
322                 vpd->vec = &sc->xlockof;
323         else
324                 vpd->vec = &sc->xof;
325
326         copy_v2_v2(&vpd->xof, vpd->vec);
327         copy_v2_v2(&vpd->xorig, &vpd->xof);
328
329         vpd->event_type = event->type;
330
331         WM_event_add_modal_handler(C, op);
332 }
333
334 static void view_pan_exit(bContext *C, wmOperator *op, int cancel)
335 {
336         ViewPanData *vpd = op->customdata;
337
338         if (cancel) {
339                 copy_v2_v2(vpd->vec, &vpd->xorig);
340
341                 ED_region_tag_redraw(CTX_wm_region(C));
342         }
343
344         WM_cursor_restore(CTX_wm_window(C));
345         MEM_freeN(op->customdata);
346 }
347
348 static int view_pan_exec(bContext *C, wmOperator *op)
349 {
350         SpaceClip *sc = CTX_wm_space_clip(C);
351         float offset[2];
352
353         RNA_float_get_array(op->ptr, "offset", offset);
354
355         if (sc->flag & SC_LOCK_SELECTION) {
356                 sc->xlockof += offset[0];
357                 sc->ylockof += offset[1];
358         }
359         else {
360                 sc->xof += offset[0];
361                 sc->yof += offset[1];
362         }
363
364         ED_region_tag_redraw(CTX_wm_region(C));
365
366         return OPERATOR_FINISHED;
367 }
368
369 static int view_pan_invoke(bContext *C, wmOperator *op, wmEvent *event)
370 {
371         if (event->type == MOUSEPAN) {
372                 SpaceClip *sc = CTX_wm_space_clip(C);
373                 float offset[2];
374
375                 offset[0] = (event->x - event->prevx) / sc->zoom;
376                 offset[1] = (event->y - event->prevy) / sc->zoom;
377
378                 RNA_float_set_array(op->ptr, "offset", offset);
379
380                 view_pan_exec(C, op);
381
382                 return OPERATOR_FINISHED;
383         }
384         else {
385                 view_pan_init(C, op, event);
386
387                 return OPERATOR_RUNNING_MODAL;
388         }
389 }
390
391 static int view_pan_modal(bContext *C, wmOperator *op, wmEvent *event)
392 {
393         SpaceClip *sc = CTX_wm_space_clip(C);
394         ViewPanData *vpd = op->customdata;
395         float offset[2];
396
397         switch (event->type) {
398                 case MOUSEMOVE:
399                         copy_v2_v2(vpd->vec, &vpd->xorig);
400                         offset[0] = (vpd->x - event->x) / sc->zoom;
401                         offset[1] = (vpd->y - event->y) / sc->zoom;
402                         RNA_float_set_array(op->ptr, "offset", offset);
403                         view_pan_exec(C, op);
404                         break;
405                 case ESCKEY:
406                         view_pan_exit(C, op, 1);
407
408                         return OPERATOR_CANCELLED;
409                 case SPACEKEY:
410                         view_pan_exit(C, op, 0);
411
412                         return OPERATOR_FINISHED;
413                 default:
414                         if (event->type == vpd->event_type && event->val == KM_RELEASE) {
415                                 view_pan_exit(C, op, 0);
416
417                                 return OPERATOR_FINISHED;
418                         }
419                         break;
420         }
421
422         return OPERATOR_RUNNING_MODAL;
423 }
424
425 static int view_pan_cancel(bContext *C, wmOperator *op)
426 {
427         view_pan_exit(C, op, 1);
428
429         return OPERATOR_CANCELLED;
430 }
431
432 void CLIP_OT_view_pan(wmOperatorType *ot)
433 {
434         /* identifiers */
435         ot->name = "View Pan";
436         ot->idname = "CLIP_OT_view_pan";
437         ot->description = "Pan the view";
438
439         /* api callbacks */
440         ot->exec = view_pan_exec;
441         ot->invoke = view_pan_invoke;
442         ot->modal = view_pan_modal;
443         ot->cancel = view_pan_cancel;
444         ot->poll = ED_space_clip_view_clip_poll;
445
446         /* flags */
447         ot->flag = OPTYPE_BLOCKING;
448
449         /* properties */
450         RNA_def_float_vector(ot->srna, "offset", 2, NULL, -FLT_MAX, FLT_MAX,
451                 "Offset", "Offset in floating point units, 1.0 is the width and height of the image", -FLT_MAX, FLT_MAX);
452 }
453
454 /********************** view zoom operator *********************/
455
456 typedef struct ViewZoomData {
457         float x, y;
458         float zoom;
459         int event_type;
460         float location[2];
461 } ViewZoomData;
462
463 static void view_zoom_init(bContext *C, wmOperator *op, wmEvent *event)
464 {
465         SpaceClip *sc = CTX_wm_space_clip(C);
466         ViewZoomData *vpd;
467
468         op->customdata = vpd = MEM_callocN(sizeof(ViewZoomData), "ClipViewZoomData");
469         WM_cursor_modal(CTX_wm_window(C), BC_NSEW_SCROLLCURSOR);
470
471         vpd->x = event->x;
472         vpd->y = event->y;
473         vpd->zoom = sc->zoom;
474         vpd->event_type = event->type;
475
476         ED_clip_mouse_pos(C, event, vpd->location);
477
478         WM_event_add_modal_handler(C, op);
479 }
480
481 static void view_zoom_exit(bContext *C, wmOperator *op, int cancel)
482 {
483         SpaceClip *sc = CTX_wm_space_clip(C);
484         ViewZoomData *vpd = op->customdata;
485
486         if (cancel) {
487                 sc->zoom = vpd->zoom;
488                 ED_region_tag_redraw(CTX_wm_region(C));
489         }
490
491         WM_cursor_restore(CTX_wm_window(C));
492         MEM_freeN(op->customdata);
493 }
494
495 static int view_zoom_exec(bContext *C, wmOperator *op)
496 {
497         SpaceClip *sc = CTX_wm_space_clip(C);
498         ARegion *ar = CTX_wm_region(C);
499
500         sclip_zoom_set_factor(sc, ar, RNA_float_get(op->ptr, "factor"), NULL);
501
502         ED_region_tag_redraw(CTX_wm_region(C));
503
504         return OPERATOR_FINISHED;
505 }
506
507 static int view_zoom_invoke(bContext *C, wmOperator *op, wmEvent *event)
508 {
509         if (event->type == MOUSEZOOM) {
510                 float factor;
511
512                 factor = 1.0f + (event->x - event->prevx + event->y - event->prevy) / 300.0f;
513                 RNA_float_set(op->ptr, "factor", factor);
514
515                 sclip_zoom_set_factor_exec(C, event, factor);
516
517                 return OPERATOR_FINISHED;
518         }
519         else {
520                 view_zoom_init(C, op, event);
521
522                 return OPERATOR_RUNNING_MODAL;
523         }
524 }
525
526 static int view_zoom_modal(bContext *C, wmOperator *op, wmEvent *event)
527 {
528         SpaceClip *sc = CTX_wm_space_clip(C);
529         ARegion *ar = CTX_wm_region(C);
530         ViewZoomData *vpd = op->customdata;
531         float factor;
532
533         switch (event->type) {
534                 case MOUSEMOVE:
535                         factor = 1.0f + (vpd->x - event->x + vpd->y - event->y) / 300.0f;
536                         RNA_float_set(op->ptr, "factor", factor);
537                         sclip_zoom_set(sc, ar, vpd->zoom * factor, vpd->location);
538                         ED_region_tag_redraw(CTX_wm_region(C));
539                         break;
540                 default:
541                         if (event->type == vpd->event_type && event->val == KM_RELEASE) {
542                                 view_zoom_exit(C, op, 0);
543
544                                 return OPERATOR_FINISHED;
545                         }
546                         break;
547         }
548
549         return OPERATOR_RUNNING_MODAL;
550 }
551
552 static int view_zoom_cancel(bContext *C, wmOperator *op)
553 {
554         view_zoom_exit(C, op, 1);
555
556         return OPERATOR_CANCELLED;
557 }
558
559 void CLIP_OT_view_zoom(wmOperatorType *ot)
560 {
561         /* identifiers */
562         ot->name = "View Zoom";
563         ot->idname = "CLIP_OT_view_zoom";
564         ot->description = "Zoom in/out the view";
565
566         /* api callbacks */
567         ot->exec = view_zoom_exec;
568         ot->invoke = view_zoom_invoke;
569         ot->modal = view_zoom_modal;
570         ot->cancel = view_zoom_cancel;
571         ot->poll = ED_space_clip_view_clip_poll;
572
573         /* flags */
574         ot->flag = OPTYPE_BLOCKING | OPTYPE_GRAB_POINTER;
575
576         /* properties */
577         RNA_def_float(ot->srna, "factor", 0.0f, 0.0f, FLT_MAX,
578                 "Factor", "Zoom factor, values higher than 1.0 zoom in, lower values zoom out", -FLT_MAX, FLT_MAX);
579 }
580
581 /********************** view zoom in/out operator *********************/
582
583 static int view_zoom_in_exec(bContext *C, wmOperator *op)
584 {
585         SpaceClip *sc = CTX_wm_space_clip(C);
586         ARegion *ar = CTX_wm_region(C);
587         float location[2];
588
589         RNA_float_get_array(op->ptr, "location", location);
590
591         sclip_zoom_set_factor(sc, ar, 1.25f, location);
592
593         ED_region_tag_redraw(CTX_wm_region(C));
594
595         return OPERATOR_FINISHED;
596 }
597
598 static int view_zoom_in_invoke(bContext *C, wmOperator *op, wmEvent *event)
599 {
600         float location[2];
601
602         ED_clip_mouse_pos(C, event, location);
603         RNA_float_set_array(op->ptr, "location", location);
604
605         return view_zoom_in_exec(C, op);
606 }
607
608 void CLIP_OT_view_zoom_in(wmOperatorType *ot)
609 {
610         /* identifiers */
611         ot->name = "View Zoom In";
612         ot->idname = "CLIP_OT_view_zoom_in";
613         ot->description = "Zoom in the view";
614
615         /* api callbacks */
616         ot->exec = view_zoom_in_exec;
617         ot->invoke = view_zoom_in_invoke;
618         ot->poll = ED_space_clip_view_clip_poll;
619
620         /* properties */
621         RNA_def_float_vector(ot->srna, "location", 2, NULL, -FLT_MAX, FLT_MAX, "Location",
622                              "Cursor location in screen coordinates", -10.0f, 10.0f);
623 }
624
625 static int view_zoom_out_exec(bContext *C, wmOperator *op)
626 {
627         SpaceClip *sc = CTX_wm_space_clip(C);
628         ARegion *ar = CTX_wm_region(C);
629         float location[2];
630
631         RNA_float_get_array(op->ptr, "location", location);
632
633         sclip_zoom_set_factor(sc, ar, 0.8f, location);
634
635         ED_region_tag_redraw(CTX_wm_region(C));
636
637         return OPERATOR_FINISHED;
638 }
639
640 static int view_zoom_out_invoke(bContext *C, wmOperator *op, wmEvent *event)
641 {
642         float location[2];
643
644         ED_clip_mouse_pos(C, event, location);
645         RNA_float_set_array(op->ptr, "location", location);
646
647         return view_zoom_out_exec(C, op);
648 }
649
650 void CLIP_OT_view_zoom_out(wmOperatorType *ot)
651 {
652         /* identifiers */
653         ot->name = "View Zoom Out";
654         ot->idname = "CLIP_OT_view_zoom_out";
655         ot->description = "Zoom out the view";
656
657         /* api callbacks */
658         ot->exec = view_zoom_out_exec;
659         ot->invoke = view_zoom_out_invoke;
660         ot->poll = ED_space_clip_view_clip_poll;
661
662         /* properties */
663         RNA_def_float_vector(ot->srna, "location", 2, NULL, -FLT_MAX, FLT_MAX, "Location",
664                              "Cursor location in normalised (0.0-1.0) coordinates", -10.0f, 10.0f);
665 }
666
667 /********************** view zoom ratio operator *********************/
668
669 static int view_zoom_ratio_exec(bContext *C, wmOperator *op)
670 {
671         SpaceClip *sc = CTX_wm_space_clip(C);
672         ARegion *ar = CTX_wm_region(C);
673
674         sclip_zoom_set(sc, ar, RNA_float_get(op->ptr, "ratio"), NULL);
675
676         /* ensure pixel exact locations for draw */
677         sc->xof = (int) sc->xof;
678         sc->yof = (int) sc->yof;
679
680         ED_region_tag_redraw(CTX_wm_region(C));
681
682         return OPERATOR_FINISHED;
683 }
684
685 void CLIP_OT_view_zoom_ratio(wmOperatorType *ot)
686 {
687         /* identifiers */
688         ot->name = "View Zoom Ratio";
689         ot->idname = "CLIP_OT_view_zoom_ratio";
690         ot->description = "Set the zoom ratio (based on clip size)";
691
692         /* api callbacks */
693         ot->exec = view_zoom_ratio_exec;
694         ot->poll = ED_space_clip_view_clip_poll;
695
696         /* properties */
697         RNA_def_float(ot->srna, "ratio", 0.0f, 0.0f, FLT_MAX,
698                 "Ratio", "Zoom ratio, 1.0 is 1:1, higher is zoomed in, lower is zoomed out", -FLT_MAX, FLT_MAX);
699 }
700
701 /********************** view all operator *********************/
702
703 static int view_all_exec(bContext *C, wmOperator *op)
704 {
705         SpaceClip *sc;
706         ARegion *ar;
707         int w, h, width, height;
708         float aspx, aspy;
709         int fit_view = RNA_boolean_get(op->ptr, "fit_view");
710         float zoomx, zoomy;
711
712         /* retrieve state */
713         sc = CTX_wm_space_clip(C);
714         ar = CTX_wm_region(C);
715
716         ED_space_clip_size(sc, &w, &h);
717         ED_space_clip_aspect(sc, &aspx, &aspy);
718
719         w = w * aspx;
720         h = h * aspy;
721
722         /* check if the image will fit in the image with zoom == 1 */
723         width = ar->winrct.xmax - ar->winrct.xmin + 1;
724         height = ar->winrct.ymax - ar->winrct.ymin + 1;
725
726         if (fit_view) {
727                 const int margin = 5; /* margin from border */
728
729                 zoomx = (float) width / (w + 2 * margin);
730                 zoomy = (float) height / (h + 2 * margin);
731
732                 sclip_zoom_set(sc, ar, MIN2(zoomx, zoomy), NULL);
733         }
734         else {
735                 if ((w >= width || h >= height) && (width > 0 && height > 0)) {
736                         zoomx = (float) width / w;
737                         zoomy = (float) height / h;
738
739                         /* find the zoom value that will fit the image in the image space */
740                         sclip_zoom_set(sc, ar, 1.0f / power_of_2(1.0f / MIN2(zoomx, zoomy)), NULL);
741                 }
742                 else
743                         sclip_zoom_set(sc, ar, 1.0f, NULL);
744         }
745
746         sc->xof = sc->yof = 0.0f;
747
748         ED_region_tag_redraw(CTX_wm_region(C));
749
750         return OPERATOR_FINISHED;
751 }
752
753 void CLIP_OT_view_all(wmOperatorType *ot)
754 {
755         /* identifiers */
756         ot->name = "View All";
757         ot->idname = "CLIP_OT_view_all";
758         ot->description = "View whole image with markers";
759
760         /* api callbacks */
761         ot->exec = view_all_exec;
762         ot->poll = ED_space_clip_view_clip_poll;
763
764         /* properties */
765         RNA_def_boolean(ot->srna, "fit_view", 0, "Fit View", "Fit frame to the viewport");
766 }
767
768 /********************** view selected operator *********************/
769
770 static int view_selected_exec(bContext *C, wmOperator *UNUSED(op))
771 {
772         SpaceClip *sc = CTX_wm_space_clip(C);
773         ARegion *ar = CTX_wm_region(C);
774
775         sc->xlockof = 0.0f;
776         sc->ylockof = 0.0f;
777
778         ED_clip_view_selection(sc, ar, 1);
779         ED_region_tag_redraw(CTX_wm_region(C));
780
781         return OPERATOR_FINISHED;
782 }
783
784 void CLIP_OT_view_selected(wmOperatorType *ot)
785 {
786         /* identifiers */
787         ot->name = "View Selected";
788         ot->idname = "CLIP_OT_view_selected";
789         ot->description = "View all selected elements";
790
791         /* api callbacks */
792         ot->exec = view_selected_exec;
793         ot->poll = ED_space_clip_view_clip_poll;
794 }
795
796 /********************** change frame operator *********************/
797
798 static int change_frame_poll(bContext *C)
799 {
800         /* prevent changes during render */
801         if (G.rendering)
802                 return 0;
803
804         return ED_space_clip_poll(C);
805 }
806
807 static void change_frame_apply(bContext *C, wmOperator *op)
808 {
809         Scene *scene = CTX_data_scene(C);
810
811         /* set the new frame number */
812         CFRA = RNA_int_get(op->ptr, "frame");
813         FRAMENUMBER_MIN_CLAMP(CFRA);
814         SUBFRA = 0.0f;
815
816         /* do updates */
817         sound_seek_scene(CTX_data_main(C), CTX_data_scene(C));
818         WM_event_add_notifier(C, NC_SCENE | ND_FRAME, scene);
819 }
820
821 static int change_frame_exec(bContext *C, wmOperator *op)
822 {
823         change_frame_apply(C, op);
824
825         return OPERATOR_FINISHED;
826 }
827
828 static int frame_from_event(bContext *C, wmEvent *event)
829 {
830         ARegion *ar = CTX_wm_region(C);
831         Scene *scene = CTX_data_scene(C);
832         int framenr = 0;
833
834         if (ar->regiontype == RGN_TYPE_WINDOW) {
835                 float sfra = SFRA, efra = EFRA, framelen = ar->winx / (efra - sfra + 1);
836
837                 framenr = sfra + event->mval[0] / framelen;
838         }
839         else {
840                 float viewx, viewy;
841
842                 UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &viewx, &viewy);
843
844                 framenr = (int) floor(viewx + 0.5f);
845         }
846
847         return framenr;
848 }
849
850 static int change_frame_invoke(bContext *C, wmOperator *op, wmEvent *event)
851 {
852         ARegion *ar = CTX_wm_region(C);
853
854         if (ar->regiontype == RGN_TYPE_WINDOW) {
855                 if (event->mval[1] > 16)
856                         return OPERATOR_PASS_THROUGH;
857         }
858
859         RNA_int_set(op->ptr, "frame", frame_from_event(C, event));
860
861         change_frame_apply(C, op);
862
863         /* add temp handler */
864         WM_event_add_modal_handler(C, op);
865
866         return OPERATOR_RUNNING_MODAL;
867 }
868
869 static int change_frame_modal(bContext *C, wmOperator *op, wmEvent *event)
870 {
871         switch (event->type) {
872                 case ESCKEY:
873                         return OPERATOR_FINISHED;
874
875                 case MOUSEMOVE:
876                         RNA_int_set(op->ptr, "frame", frame_from_event(C, event));
877                         change_frame_apply(C, op);
878                         break;
879
880                 case LEFTMOUSE:
881                 case RIGHTMOUSE:
882                         if (event->val == KM_RELEASE)
883                                 return OPERATOR_FINISHED;
884                         break;
885         }
886
887         return OPERATOR_RUNNING_MODAL;
888 }
889
890 void CLIP_OT_change_frame(wmOperatorType *ot)
891 {
892         /* identifiers */
893         ot->name = "Change frame";
894         ot->idname = "CLIP_OT_change_frame";
895         ot->description = "Interactively change the current frame number";
896
897         /* api callbacks */
898         ot->exec = change_frame_exec;
899         ot->invoke = change_frame_invoke;
900         ot->modal = change_frame_modal;
901         ot->poll = change_frame_poll;
902
903         /* flags */
904         ot->flag = OPTYPE_BLOCKING | OPTYPE_UNDO;
905
906         /* rna */
907         RNA_def_int(ot->srna, "frame", 0, MINAFRAME, MAXFRAME, "Frame", "", MINAFRAME, MAXFRAME);
908 }
909
910 /********************** rebuild proxies operator *********************/
911
912 typedef struct ProxyBuildJob {
913         Scene *scene;
914         struct Main *main;
915         MovieClip *clip;
916         int clip_flag, stop;
917         struct IndexBuildContext *index_context;
918 } ProxyJob;
919
920 static void proxy_freejob(void *pjv)
921 {
922         ProxyJob *pj = pjv;
923
924         MEM_freeN(pj);
925 }
926
927 static int proxy_bitflag_to_array(int size_flag, int build_sizes[4], int undistort)
928 {
929         int build_count = 0;
930         int size_flags[2][4] = {{MCLIP_PROXY_SIZE_25,
931                                  MCLIP_PROXY_SIZE_50,
932                                  MCLIP_PROXY_SIZE_75,
933                                  MCLIP_PROXY_SIZE_100},
934                                 {MCLIP_PROXY_UNDISTORTED_SIZE_25,
935                                  MCLIP_PROXY_UNDISTORTED_SIZE_50,
936                                  MCLIP_PROXY_UNDISTORTED_SIZE_75,
937                                  MCLIP_PROXY_UNDISTORTED_SIZE_100}};
938         int size_nr = undistort ? 1 : 0;
939
940         if (size_flag & size_flags[size_nr][0])
941                 build_sizes[build_count++] = MCLIP_PROXY_RENDER_SIZE_25;
942
943         if (size_flag & size_flags[size_nr][1])
944                 build_sizes[build_count++] = MCLIP_PROXY_RENDER_SIZE_50;
945
946         if (size_flag & size_flags[size_nr][2])
947                 build_sizes[build_count++] = MCLIP_PROXY_RENDER_SIZE_75;
948
949         if (size_flag & size_flags[size_nr][3])
950                 build_sizes[build_count++] = MCLIP_PROXY_RENDER_SIZE_100;
951
952         return build_count;
953 }
954
955 /* only this runs inside thread */
956 static void proxy_startjob(void *pjv, short *stop, short *do_update, float *progress)
957 {
958         ProxyJob *pj = pjv;
959         Scene *scene = pj->scene;
960         MovieClip *clip = pj->clip;
961         struct MovieDistortion *distortion = NULL;
962         short size_flag;
963         int cfra, sfra = SFRA, efra = EFRA;
964         int build_sizes[4], build_count = 0;
965         int build_undistort_sizes[4], build_undistort_count = 0;
966
967         size_flag = clip->proxy.build_size_flag;
968
969         build_count = proxy_bitflag_to_array(size_flag, build_sizes, 0);
970         build_undistort_count = proxy_bitflag_to_array(size_flag, build_undistort_sizes, 1);
971
972         if (clip->source == MCLIP_SRC_MOVIE) {
973                 if (pj->index_context)
974                         IMB_anim_index_rebuild(pj->index_context, stop, do_update, progress);
975
976                 if (!build_undistort_count) {
977                         if (*stop)
978                                 pj->stop = 1;
979
980                         return;
981                 }
982                 else {
983                         sfra = 1;
984                         efra = IMB_anim_get_duration(clip->anim, IMB_TC_NONE);
985                 }
986         }
987
988         if (build_undistort_count)
989                 distortion = BKE_tracking_distortion_new();
990
991         for (cfra = sfra; cfra <= efra; cfra++) {
992                 if (clip->source != MCLIP_SRC_MOVIE)
993                         BKE_movieclip_build_proxy_frame(clip, pj->clip_flag, NULL, cfra, build_sizes, build_count, 0);
994
995                 BKE_movieclip_build_proxy_frame(clip, pj->clip_flag, distortion, cfra,
996                                                 build_undistort_sizes, build_undistort_count, 1);
997
998                 if (*stop || G.afbreek)
999                         break;
1000
1001                 *do_update = TRUE;
1002                 *progress = ((float) cfra) / (efra - sfra);
1003         }
1004
1005         if (distortion)
1006                 BKE_tracking_distortion_free(distortion);
1007
1008         if (*stop)
1009                 pj->stop = 1;
1010 }
1011
1012 static void proxy_endjob(void *pjv)
1013 {
1014         ProxyJob *pj = pjv;
1015
1016         if (pj->clip->anim)
1017                 IMB_close_anim_proxies(pj->clip->anim);
1018
1019         if (pj->index_context)
1020                 IMB_anim_index_rebuild_finish(pj->index_context, pj->stop);
1021
1022         BKE_movieclip_reload(pj->clip);
1023
1024         WM_main_add_notifier(NC_MOVIECLIP | ND_DISPLAY, pj->clip);
1025 }
1026
1027 static int clip_rebuild_proxy_exec(bContext *C, wmOperator *UNUSED(op))
1028 {
1029         wmJob * steve;
1030         ProxyJob *pj;
1031         Scene *scene = CTX_data_scene(C);
1032         ScrArea *sa = CTX_wm_area(C);
1033         SpaceClip *sc = CTX_wm_space_clip(C);
1034         MovieClip *clip = ED_space_clip(sc);
1035
1036         if ((clip->flag & MCLIP_USE_PROXY) == 0)
1037                 return OPERATOR_CANCELLED;
1038
1039         steve = WM_jobs_get(CTX_wm_manager(C), CTX_wm_window(C), sa, "Building Proxies", WM_JOB_PROGRESS);
1040
1041         pj = MEM_callocN(sizeof(ProxyJob), "proxy rebuild job");
1042         pj->scene = scene;
1043         pj->main = CTX_data_main(C);
1044         pj->clip = clip;
1045         pj->clip_flag = clip->flag & MCLIP_TIMECODE_FLAGS;
1046
1047         if (clip->anim) {
1048                 pj->index_context = IMB_anim_index_rebuild_context(clip->anim, clip->proxy.build_tc_flag,
1049                                         clip->proxy.build_size_flag, clip->proxy.quality);
1050         }
1051
1052         WM_jobs_customdata(steve, pj, proxy_freejob);
1053         WM_jobs_timer(steve, 0.2, NC_MOVIECLIP | ND_DISPLAY, 0);
1054         WM_jobs_callbacks(steve, proxy_startjob, NULL, NULL, proxy_endjob);
1055
1056         G.afbreek = 0;
1057         WM_jobs_start(CTX_wm_manager(C), steve);
1058
1059         ED_area_tag_redraw(CTX_wm_area(C));
1060
1061         return OPERATOR_FINISHED;
1062 }
1063
1064 void CLIP_OT_rebuild_proxy(wmOperatorType *ot)
1065 {
1066         /* identifiers */
1067         ot->name = "Rebuild Proxy and Timecode Indices";
1068         ot->idname = "CLIP_OT_rebuild_proxy";
1069         ot->description = "Rebuild all selected proxies and timecode indices in the background";
1070
1071         /* api callbacks */
1072         ot->exec = clip_rebuild_proxy_exec;
1073         ot->poll = ED_space_clip_poll;
1074
1075         /* flags */
1076         ot->flag = OPTYPE_REGISTER;
1077 }
1078
1079 /********************** mode set operator *********************/
1080
1081 static int mode_set_exec(bContext *C, wmOperator *op)
1082 {
1083         SpaceClip *sc = CTX_wm_space_clip(C);
1084         int mode = RNA_enum_get(op->ptr, "mode");
1085         int toggle = RNA_boolean_get(op->ptr, "toggle");
1086
1087         if (sc->mode == mode) {
1088                 if (toggle)
1089                         sc->mode = SC_MODE_TRACKING;
1090         }
1091         else {
1092                 sc->mode = mode;
1093         }
1094
1095         WM_event_add_notifier(C, NC_SPACE | ND_SPACE_CLIP, NULL);
1096
1097         return OPERATOR_FINISHED;
1098 }
1099
1100 void CLIP_OT_mode_set(wmOperatorType *ot)
1101 {
1102         static EnumPropertyItem mode_items[] = {
1103                 {SC_MODE_TRACKING, "TRACKING", 0, "Tracking", "Show tracking and solving tools"},
1104                 {SC_MODE_RECONSTRUCTION, "RECONSTRUCTION", 0, "Reconstruction", "Show tracking/reconstruction tools"},
1105                 {SC_MODE_DISTORTION, "DISTORTION", 0, "Distortion", "Show distortion tools"},
1106                 {0, NULL, 0, NULL, NULL}};
1107
1108
1109         /* identifiers */
1110         ot->name = "Set Clip Mode";
1111         ot->description = "Set the clip interaction mode";
1112         ot->idname = "CLIP_OT_mode_set";
1113
1114         /* api callbacks */
1115         ot->exec = mode_set_exec;
1116
1117         ot->poll = ED_space_clip_poll;
1118
1119         /* properties */
1120         RNA_def_enum(ot->srna, "mode", mode_items, SC_MODE_TRACKING, "Mode", "");
1121         RNA_def_boolean(ot->srna, "toggle", 0, "Toggle", "");
1122 }
1123
1124 /********************** macroses *********************/
1125
1126 void ED_operatormacros_clip(void)
1127 {
1128         wmOperatorType *ot;
1129         wmOperatorTypeMacro *otmacro;
1130
1131         ot = WM_operatortype_append_macro("CLIP_OT_add_marker_move", "Add Marker and Move",
1132                                           "Add new marker and move it on movie", OPTYPE_UNDO | OPTYPE_REGISTER);
1133         WM_operatortype_macro_define(ot, "CLIP_OT_add_marker");
1134         otmacro = WM_operatortype_macro_define(ot, "TRANSFORM_OT_translate");
1135         RNA_struct_idprops_unset(otmacro->ptr, "release_confirm");
1136
1137         ot = WM_operatortype_append_macro("CLIP_OT_add_marker_slide", "Add Marker and Slide",
1138                                           "Add new marker and slide it with mouse until mouse button release",
1139                                           OPTYPE_UNDO | OPTYPE_REGISTER);
1140         WM_operatortype_macro_define(ot, "CLIP_OT_add_marker");
1141         otmacro = WM_operatortype_macro_define(ot, "TRANSFORM_OT_translate");
1142         RNA_boolean_set(otmacro->ptr, "release_confirm", TRUE);
1143 }