Cleanup: style, use braces for editors
[blender.git] / source / blender / editors / space_clip / clip_ops.c
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  *
16  * The Original Code is Copyright (C) 2011 Blender Foundation.
17  * All rights reserved.
18  */
19
20 /** \file
21  * \ingroup spclip
22  */
23
24 #include <errno.h>
25 #include <sys/types.h>
26 #include <fcntl.h>
27
28 #ifndef WIN32
29 #  include <unistd.h>
30 #else
31 #  include <io.h>
32 #endif
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_utildefines.h"
40 #include "BLI_fileops.h"
41 #include "BLI_path_util.h"
42 #include "BLI_math.h"
43 #include "BLI_rect.h"
44 #include "BLI_task.h"
45 #include "BLI_string.h"
46
47 #include "BLT_translation.h"
48
49 #include "BKE_context.h"
50 #include "BKE_global.h"
51 #include "BKE_library.h"
52 #include "BKE_main.h"
53 #include "BKE_movieclip.h"
54 #include "BKE_report.h"
55 #include "BKE_sound.h"
56 #include "BKE_tracking.h"
57
58 #include "WM_api.h"
59 #include "WM_types.h"
60
61 #include "IMB_imbuf_types.h"
62 #include "IMB_imbuf.h"
63
64 #include "ED_screen.h"
65 #include "ED_clip.h"
66
67 #include "UI_interface.h"
68
69 #include "RNA_access.h"
70 #include "RNA_define.h"
71 #include "RNA_enum_types.h"
72
73 #include "UI_view2d.h"
74
75 #include "PIL_time.h"
76
77 #include "DEG_depsgraph_build.h"
78
79 #include "clip_intern.h"  // own include
80
81 /******************** view navigation utilities *********************/
82
83 static void sclip_zoom_set(const bContext *C, float zoom, float location[2])
84 {
85   SpaceClip *sc = CTX_wm_space_clip(C);
86   ARegion *ar = CTX_wm_region(C);
87
88   float oldzoom = sc->zoom;
89   int width, height;
90
91   sc->zoom = zoom;
92
93   if (sc->zoom < 0.1f || sc->zoom > 4.0f) {
94     /* check zoom limits */
95     ED_space_clip_get_size(sc, &width, &height);
96
97     width *= sc->zoom;
98     height *= sc->zoom;
99
100     if ((width < 4) && (height < 4) && sc->zoom < oldzoom) {
101       sc->zoom = oldzoom;
102     }
103     else if (BLI_rcti_size_x(&ar->winrct) <= sc->zoom) {
104       sc->zoom = oldzoom;
105     }
106     else if (BLI_rcti_size_y(&ar->winrct) <= sc->zoom) {
107       sc->zoom = oldzoom;
108     }
109   }
110
111   if ((U.uiflag & USER_ZOOM_TO_MOUSEPOS) && location) {
112     float aspx, aspy, w, h, dx, dy;
113
114     ED_space_clip_get_size(sc, &width, &height);
115     ED_space_clip_get_aspect(sc, &aspx, &aspy);
116
117     w = width * aspx;
118     h = height * aspy;
119
120     dx = ((location[0] - 0.5f) * w - sc->xof) * (sc->zoom - oldzoom) / sc->zoom;
121     dy = ((location[1] - 0.5f) * h - sc->yof) * (sc->zoom - oldzoom) / sc->zoom;
122
123     if (sc->flag & SC_LOCK_SELECTION) {
124       sc->xlockof += dx;
125       sc->ylockof += dy;
126     }
127     else {
128       sc->xof += dx;
129       sc->yof += dy;
130     }
131   }
132 }
133
134 static void sclip_zoom_set_factor(const bContext *C, float zoomfac, float location[2])
135 {
136   SpaceClip *sc = CTX_wm_space_clip(C);
137
138   sclip_zoom_set(C, sc->zoom * zoomfac, location);
139 }
140
141 static void sclip_zoom_set_factor_exec(bContext *C, const wmEvent *event, float factor)
142 {
143   ARegion *ar = CTX_wm_region(C);
144
145   float location[2], *mpos = NULL;
146
147   if (event) {
148     SpaceClip *sc = CTX_wm_space_clip(C);
149
150     ED_clip_mouse_pos(sc, ar, event->mval, location);
151     mpos = location;
152   }
153
154   sclip_zoom_set_factor(C, factor, mpos);
155
156   ED_region_tag_redraw(ar);
157 }
158
159 /******************** open clip operator ********************/
160
161 static void clip_filesel(bContext *C, wmOperator *op, const char *path)
162 {
163   RNA_string_set(op->ptr, "directory", path);
164
165   WM_event_add_fileselect(C, op);
166 }
167
168 static void open_init(bContext *C, wmOperator *op)
169 {
170   PropertyPointerRNA *pprop;
171
172   op->customdata = pprop = MEM_callocN(sizeof(PropertyPointerRNA), "OpenPropertyPointerRNA");
173   UI_context_active_but_prop_get_templateID(C, &pprop->ptr, &pprop->prop);
174 }
175
176 static void open_cancel(bContext *UNUSED(C), wmOperator *op)
177 {
178   MEM_freeN(op->customdata);
179   op->customdata = NULL;
180 }
181
182 static int open_exec(bContext *C, wmOperator *op)
183 {
184   SpaceClip *sc = CTX_wm_space_clip(C);
185   bScreen *screen = CTX_wm_screen(C);
186   Main *bmain = CTX_data_main(C);
187   PropertyPointerRNA *pprop;
188   PointerRNA idptr;
189   MovieClip *clip = NULL;
190   char str[FILE_MAX];
191
192   if (RNA_collection_length(op->ptr, "files")) {
193     PointerRNA fileptr;
194     PropertyRNA *prop;
195     char dir_only[FILE_MAX], file_only[FILE_MAX];
196     bool relative = RNA_boolean_get(op->ptr, "relative_path");
197
198     RNA_string_get(op->ptr, "directory", dir_only);
199     if (relative) {
200       BLI_path_rel(dir_only, CTX_data_main(C)->name);
201     }
202
203     prop = RNA_struct_find_property(op->ptr, "files");
204     RNA_property_collection_lookup_int(op->ptr, prop, 0, &fileptr);
205     RNA_string_get(&fileptr, "name", file_only);
206
207     BLI_join_dirfile(str, sizeof(str), dir_only, file_only);
208   }
209   else {
210     BKE_report(op->reports, RPT_ERROR, "No files selected to be opened");
211
212     return OPERATOR_CANCELLED;
213   }
214
215   /* default to frame 1 if there's no scene in context */
216
217   errno = 0;
218
219   clip = BKE_movieclip_file_add_exists(bmain, str);
220
221   if (!clip) {
222     if (op->customdata) {
223       MEM_freeN(op->customdata);
224     }
225
226     BKE_reportf(op->reports,
227                 RPT_ERROR,
228                 "Cannot read '%s': %s",
229                 str,
230                 errno ? strerror(errno) : TIP_("unsupported movie clip format"));
231
232     return OPERATOR_CANCELLED;
233   }
234
235   if (!op->customdata) {
236     open_init(C, op);
237   }
238
239   /* hook into UI */
240   pprop = op->customdata;
241
242   if (pprop->prop) {
243     /* when creating new ID blocks, use is already 1, but RNA
244      * pointer use also increases user, so this compensates it */
245     id_us_min(&clip->id);
246
247     RNA_id_pointer_create(&clip->id, &idptr);
248     RNA_property_pointer_set(&pprop->ptr, pprop->prop, idptr);
249     RNA_property_update(C, &pprop->ptr, pprop->prop);
250   }
251   else if (sc) {
252     ED_space_clip_set_clip(C, screen, sc, clip);
253   }
254
255   WM_event_add_notifier(C, NC_MOVIECLIP | NA_ADDED, clip);
256
257   DEG_relations_tag_update(bmain);
258   MEM_freeN(op->customdata);
259
260   return OPERATOR_FINISHED;
261 }
262
263 static int open_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
264 {
265   SpaceClip *sc = CTX_wm_space_clip(C);
266   char path[FILE_MAX];
267   MovieClip *clip = NULL;
268
269   if (sc) {
270     clip = ED_space_clip_get_clip(sc);
271   }
272
273   if (clip) {
274     BLI_strncpy(path, clip->name, sizeof(path));
275
276     BLI_path_abs(path, CTX_data_main(C)->name);
277     BLI_parent_dir(path);
278   }
279   else {
280     BLI_strncpy(path, U.textudir, sizeof(path));
281   }
282
283   if (RNA_struct_property_is_set(op->ptr, "files")) {
284     return open_exec(C, op);
285   }
286
287   if (!RNA_struct_property_is_set(op->ptr, "relative_path")) {
288     RNA_boolean_set(op->ptr, "relative_path", (U.flag & USER_RELPATHS) != 0);
289   }
290
291   open_init(C, op);
292
293   clip_filesel(C, op, path);
294
295   return OPERATOR_RUNNING_MODAL;
296 }
297
298 void CLIP_OT_open(wmOperatorType *ot)
299 {
300   /* identifiers */
301   ot->name = "Open Clip";
302   ot->description = "Load a sequence of frames or a movie file";
303   ot->idname = "CLIP_OT_open";
304
305   /* api callbacks */
306   ot->exec = open_exec;
307   ot->invoke = open_invoke;
308   ot->cancel = open_cancel;
309
310   /* flags */
311   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
312
313   /* properties */
314   WM_operator_properties_filesel(ot,
315                                  FILE_TYPE_FOLDER | FILE_TYPE_IMAGE | FILE_TYPE_MOVIE,
316                                  FILE_SPECIAL,
317                                  FILE_OPENFILE,
318                                  WM_FILESEL_RELPATH | WM_FILESEL_FILES | WM_FILESEL_DIRECTORY,
319                                  FILE_DEFAULTDISPLAY,
320                                  FILE_SORT_ALPHA);
321 }
322
323 /******************* reload clip operator *********************/
324
325 static int reload_exec(bContext *C, wmOperator *UNUSED(op))
326 {
327   MovieClip *clip = CTX_data_edit_movieclip(C);
328
329   if (!clip) {
330     return OPERATOR_CANCELLED;
331   }
332
333   WM_jobs_kill_type(CTX_wm_manager(C), NULL, WM_JOB_TYPE_CLIP_PREFETCH);
334   BKE_movieclip_reload(CTX_data_main(C), clip);
335
336   WM_event_add_notifier(C, NC_MOVIECLIP | NA_EDITED, clip);
337
338   return OPERATOR_FINISHED;
339 }
340
341 void CLIP_OT_reload(wmOperatorType *ot)
342 {
343   /* identifiers */
344   ot->name = "Reload Clip";
345   ot->description = "Reload clip";
346   ot->idname = "CLIP_OT_reload";
347
348   /* api callbacks */
349   ot->exec = reload_exec;
350 }
351
352 /********************** view pan operator *********************/
353
354 typedef struct ViewPanData {
355   float x, y;
356   float xof, yof, xorig, yorig;
357   int event_type;
358   float *vec;
359 } ViewPanData;
360
361 static void view_pan_init(bContext *C, wmOperator *op, const wmEvent *event)
362 {
363   SpaceClip *sc = CTX_wm_space_clip(C);
364   ViewPanData *vpd;
365
366   op->customdata = vpd = MEM_callocN(sizeof(ViewPanData), "ClipViewPanData");
367   WM_cursor_modal_set(CTX_wm_window(C), BC_NSEW_SCROLLCURSOR);
368
369   vpd->x = event->x;
370   vpd->y = event->y;
371
372   if (sc->flag & SC_LOCK_SELECTION) {
373     vpd->vec = &sc->xlockof;
374   }
375   else {
376     vpd->vec = &sc->xof;
377   }
378
379   copy_v2_v2(&vpd->xof, vpd->vec);
380   copy_v2_v2(&vpd->xorig, &vpd->xof);
381
382   vpd->event_type = event->type;
383
384   WM_event_add_modal_handler(C, op);
385 }
386
387 static void view_pan_exit(bContext *C, wmOperator *op, bool cancel)
388 {
389   ViewPanData *vpd = op->customdata;
390
391   if (cancel) {
392     copy_v2_v2(vpd->vec, &vpd->xorig);
393
394     ED_region_tag_redraw(CTX_wm_region(C));
395   }
396
397   WM_cursor_modal_restore(CTX_wm_window(C));
398   MEM_freeN(op->customdata);
399 }
400
401 static int view_pan_exec(bContext *C, wmOperator *op)
402 {
403   SpaceClip *sc = CTX_wm_space_clip(C);
404   float offset[2];
405
406   RNA_float_get_array(op->ptr, "offset", offset);
407
408   if (sc->flag & SC_LOCK_SELECTION) {
409     sc->xlockof += offset[0];
410     sc->ylockof += offset[1];
411   }
412   else {
413     sc->xof += offset[0];
414     sc->yof += offset[1];
415   }
416
417   ED_region_tag_redraw(CTX_wm_region(C));
418
419   return OPERATOR_FINISHED;
420 }
421
422 static int view_pan_invoke(bContext *C, wmOperator *op, const wmEvent *event)
423 {
424   if (event->type == MOUSEPAN) {
425     SpaceClip *sc = CTX_wm_space_clip(C);
426     float offset[2];
427
428     offset[0] = (event->prevx - event->x) / sc->zoom;
429     offset[1] = (event->prevy - event->y) / sc->zoom;
430
431     RNA_float_set_array(op->ptr, "offset", offset);
432
433     view_pan_exec(C, op);
434
435     return OPERATOR_FINISHED;
436   }
437   else {
438     view_pan_init(C, op, event);
439
440     return OPERATOR_RUNNING_MODAL;
441   }
442 }
443
444 static int view_pan_modal(bContext *C, wmOperator *op, const wmEvent *event)
445 {
446   SpaceClip *sc = CTX_wm_space_clip(C);
447   ViewPanData *vpd = op->customdata;
448   float offset[2];
449
450   switch (event->type) {
451     case MOUSEMOVE:
452       copy_v2_v2(vpd->vec, &vpd->xorig);
453       offset[0] = (vpd->x - event->x) / sc->zoom;
454       offset[1] = (vpd->y - event->y) / sc->zoom;
455       RNA_float_set_array(op->ptr, "offset", offset);
456       view_pan_exec(C, op);
457       break;
458     case ESCKEY:
459       view_pan_exit(C, op, 1);
460
461       return OPERATOR_CANCELLED;
462     case SPACEKEY:
463       view_pan_exit(C, op, 0);
464
465       return OPERATOR_FINISHED;
466     default:
467       if (event->type == vpd->event_type && event->val == KM_RELEASE) {
468         view_pan_exit(C, op, 0);
469
470         return OPERATOR_FINISHED;
471       }
472       break;
473   }
474
475   return OPERATOR_RUNNING_MODAL;
476 }
477
478 static void view_pan_cancel(bContext *C, wmOperator *op)
479 {
480   view_pan_exit(C, op, true);
481 }
482
483 void CLIP_OT_view_pan(wmOperatorType *ot)
484 {
485   /* identifiers */
486   ot->name = "Pan View";
487   ot->idname = "CLIP_OT_view_pan";
488   ot->description = "Pan the view";
489
490   /* api callbacks */
491   ot->exec = view_pan_exec;
492   ot->invoke = view_pan_invoke;
493   ot->modal = view_pan_modal;
494   ot->cancel = view_pan_cancel;
495   ot->poll = ED_space_clip_view_clip_poll;
496
497   /* flags */
498   ot->flag = OPTYPE_BLOCKING | OPTYPE_GRAB_CURSOR;
499
500   /* properties */
501   RNA_def_float_vector(ot->srna,
502                        "offset",
503                        2,
504                        NULL,
505                        -FLT_MAX,
506                        FLT_MAX,
507                        "Offset",
508                        "Offset in floating point units, 1.0 is the width and height of the image",
509                        -FLT_MAX,
510                        FLT_MAX);
511 }
512
513 /********************** view zoom operator *********************/
514
515 typedef struct ViewZoomData {
516   float x, y;
517   float zoom;
518   int event_type;
519   float location[2];
520   wmTimer *timer;
521   double timer_lastdraw;
522 } ViewZoomData;
523
524 static void view_zoom_init(bContext *C, wmOperator *op, const wmEvent *event)
525 {
526   SpaceClip *sc = CTX_wm_space_clip(C);
527   ARegion *ar = CTX_wm_region(C);
528   ViewZoomData *vpd;
529
530   op->customdata = vpd = MEM_callocN(sizeof(ViewZoomData), "ClipViewZoomData");
531   WM_cursor_modal_set(CTX_wm_window(C), BC_NSEW_SCROLLCURSOR);
532
533   if (U.viewzoom == USER_ZOOM_CONT) {
534     /* needs a timer to continue redrawing */
535     vpd->timer = WM_event_add_timer(CTX_wm_manager(C), CTX_wm_window(C), TIMER, 0.01f);
536     vpd->timer_lastdraw = PIL_check_seconds_timer();
537   }
538
539   vpd->x = event->x;
540   vpd->y = event->y;
541   vpd->zoom = sc->zoom;
542   vpd->event_type = event->type;
543
544   ED_clip_mouse_pos(sc, ar, event->mval, vpd->location);
545
546   WM_event_add_modal_handler(C, op);
547 }
548
549 static void view_zoom_exit(bContext *C, wmOperator *op, bool cancel)
550 {
551   SpaceClip *sc = CTX_wm_space_clip(C);
552   ViewZoomData *vpd = op->customdata;
553
554   if (cancel) {
555     sc->zoom = vpd->zoom;
556     ED_region_tag_redraw(CTX_wm_region(C));
557   }
558
559   if (vpd->timer) {
560     WM_event_remove_timer(CTX_wm_manager(C), vpd->timer->win, vpd->timer);
561   }
562
563   WM_cursor_modal_restore(CTX_wm_window(C));
564   MEM_freeN(op->customdata);
565 }
566
567 static int view_zoom_exec(bContext *C, wmOperator *op)
568 {
569   sclip_zoom_set_factor(C, RNA_float_get(op->ptr, "factor"), NULL);
570
571   ED_region_tag_redraw(CTX_wm_region(C));
572
573   return OPERATOR_FINISHED;
574 }
575
576 static int view_zoom_invoke(bContext *C, wmOperator *op, const wmEvent *event)
577 {
578   if (event->type == MOUSEZOOM || event->type == MOUSEPAN) {
579     float delta, factor;
580
581     delta = event->prevx - event->x + event->prevy - event->y;
582
583     if (U.uiflag & USER_ZOOM_INVERT) {
584       delta *= -1;
585     }
586
587     factor = 1.0f + delta / 300.0f;
588     RNA_float_set(op->ptr, "factor", factor);
589
590     sclip_zoom_set_factor_exec(C, event, factor);
591
592     return OPERATOR_FINISHED;
593   }
594   else {
595     view_zoom_init(C, op, event);
596
597     return OPERATOR_RUNNING_MODAL;
598   }
599 }
600
601 static void view_zoom_apply(bContext *C, ViewZoomData *vpd, wmOperator *op, const wmEvent *event)
602 {
603   float factor;
604
605   if (U.viewzoom == USER_ZOOM_CONT) {
606     SpaceClip *sclip = CTX_wm_space_clip(C);
607     double time = PIL_check_seconds_timer();
608     float time_step = (float)(time - vpd->timer_lastdraw);
609     float fac;
610     float zfac;
611
612     if (U.uiflag & USER_ZOOM_HORIZ) {
613       fac = (float)(event->x - vpd->x);
614     }
615     else {
616       fac = (float)(event->y - vpd->y);
617     }
618
619     if (U.uiflag & USER_ZOOM_INVERT) {
620       fac = -fac;
621     }
622
623     zfac = 1.0f + ((fac / 20.0f) * time_step);
624     vpd->timer_lastdraw = time;
625     factor = (sclip->zoom * zfac) / vpd->zoom;
626   }
627   else {
628     float delta = event->x - vpd->x + event->y - vpd->y;
629
630     if (U.uiflag & USER_ZOOM_INVERT) {
631       delta *= -1;
632     }
633
634     factor = 1.0f + delta / 300.0f;
635   }
636
637   RNA_float_set(op->ptr, "factor", factor);
638   sclip_zoom_set(C, vpd->zoom * factor, vpd->location);
639   ED_region_tag_redraw(CTX_wm_region(C));
640 }
641
642 static int view_zoom_modal(bContext *C, wmOperator *op, const wmEvent *event)
643 {
644   ViewZoomData *vpd = op->customdata;
645   switch (event->type) {
646     case TIMER:
647       if (event->customdata == vpd->timer) {
648         view_zoom_apply(C, vpd, op, event);
649       }
650       break;
651     case MOUSEMOVE:
652       view_zoom_apply(C, vpd, op, event);
653       break;
654     default:
655       if (event->type == vpd->event_type && event->val == KM_RELEASE) {
656         view_zoom_exit(C, op, 0);
657
658         return OPERATOR_FINISHED;
659       }
660       break;
661   }
662
663   return OPERATOR_RUNNING_MODAL;
664 }
665
666 static void view_zoom_cancel(bContext *C, wmOperator *op)
667 {
668   view_zoom_exit(C, op, true);
669 }
670
671 void CLIP_OT_view_zoom(wmOperatorType *ot)
672 {
673   PropertyRNA *prop;
674
675   /* identifiers */
676   ot->name = "View Zoom";
677   ot->idname = "CLIP_OT_view_zoom";
678   ot->description = "Zoom in/out the view";
679
680   /* api callbacks */
681   ot->exec = view_zoom_exec;
682   ot->invoke = view_zoom_invoke;
683   ot->modal = view_zoom_modal;
684   ot->cancel = view_zoom_cancel;
685   ot->poll = ED_space_clip_view_clip_poll;
686
687   /* flags */
688   ot->flag = OPTYPE_BLOCKING | OPTYPE_GRAB_CURSOR;
689
690   /* properties */
691   prop = RNA_def_float(ot->srna,
692                        "factor",
693                        0.0f,
694                        -FLT_MAX,
695                        FLT_MAX,
696                        "Factor",
697                        "Zoom factor, values higher than 1.0 zoom in, lower values zoom out",
698                        -FLT_MAX,
699                        FLT_MAX);
700   RNA_def_property_flag(prop, PROP_HIDDEN);
701 }
702
703 /********************** view zoom in/out operator *********************/
704
705 static int view_zoom_in_exec(bContext *C, wmOperator *op)
706 {
707   float location[2];
708
709   RNA_float_get_array(op->ptr, "location", location);
710
711   sclip_zoom_set_factor(C, powf(2.0f, 1.0f / 3.0f), location);
712
713   ED_region_tag_redraw(CTX_wm_region(C));
714
715   return OPERATOR_FINISHED;
716 }
717
718 static int view_zoom_in_invoke(bContext *C, wmOperator *op, const wmEvent *event)
719 {
720   SpaceClip *sc = CTX_wm_space_clip(C);
721   ARegion *ar = CTX_wm_region(C);
722
723   float location[2];
724
725   ED_clip_mouse_pos(sc, ar, event->mval, location);
726   RNA_float_set_array(op->ptr, "location", location);
727
728   return view_zoom_in_exec(C, op);
729 }
730
731 void CLIP_OT_view_zoom_in(wmOperatorType *ot)
732 {
733   PropertyRNA *prop;
734
735   /* identifiers */
736   ot->name = "View Zoom In";
737   ot->idname = "CLIP_OT_view_zoom_in";
738   ot->description = "Zoom in the view";
739
740   /* api callbacks */
741   ot->exec = view_zoom_in_exec;
742   ot->invoke = view_zoom_in_invoke;
743   ot->poll = ED_space_clip_view_clip_poll;
744
745   /* properties */
746   prop = RNA_def_float_vector(ot->srna,
747                               "location",
748                               2,
749                               NULL,
750                               -FLT_MAX,
751                               FLT_MAX,
752                               "Location",
753                               "Cursor location in screen coordinates",
754                               -10.0f,
755                               10.0f);
756   RNA_def_property_flag(prop, PROP_HIDDEN);
757 }
758
759 static int view_zoom_out_exec(bContext *C, wmOperator *op)
760 {
761   float location[2];
762
763   RNA_float_get_array(op->ptr, "location", location);
764
765   sclip_zoom_set_factor(C, powf(0.5f, 1.0f / 3.0f), location);
766
767   ED_region_tag_redraw(CTX_wm_region(C));
768
769   return OPERATOR_FINISHED;
770 }
771
772 static int view_zoom_out_invoke(bContext *C, wmOperator *op, const wmEvent *event)
773 {
774   SpaceClip *sc = CTX_wm_space_clip(C);
775   ARegion *ar = CTX_wm_region(C);
776
777   float location[2];
778
779   ED_clip_mouse_pos(sc, ar, event->mval, location);
780   RNA_float_set_array(op->ptr, "location", location);
781
782   return view_zoom_out_exec(C, op);
783 }
784
785 void CLIP_OT_view_zoom_out(wmOperatorType *ot)
786 {
787   PropertyRNA *prop;
788
789   /* identifiers */
790   ot->name = "View Zoom Out";
791   ot->idname = "CLIP_OT_view_zoom_out";
792   ot->description = "Zoom out the view";
793
794   /* api callbacks */
795   ot->exec = view_zoom_out_exec;
796   ot->invoke = view_zoom_out_invoke;
797   ot->poll = ED_space_clip_view_clip_poll;
798
799   /* properties */
800   prop = RNA_def_float_vector(ot->srna,
801                               "location",
802                               2,
803                               NULL,
804                               -FLT_MAX,
805                               FLT_MAX,
806                               "Location",
807                               "Cursor location in normalized (0.0-1.0) coordinates",
808                               -10.0f,
809                               10.0f);
810   RNA_def_property_flag(prop, PROP_HIDDEN);
811 }
812
813 /********************** view zoom ratio operator *********************/
814
815 static int view_zoom_ratio_exec(bContext *C, wmOperator *op)
816 {
817   SpaceClip *sc = CTX_wm_space_clip(C);
818
819   sclip_zoom_set(C, RNA_float_get(op->ptr, "ratio"), NULL);
820
821   /* ensure pixel exact locations for draw */
822   sc->xof = (int)sc->xof;
823   sc->yof = (int)sc->yof;
824
825   ED_region_tag_redraw(CTX_wm_region(C));
826
827   return OPERATOR_FINISHED;
828 }
829
830 void CLIP_OT_view_zoom_ratio(wmOperatorType *ot)
831 {
832   /* identifiers */
833   ot->name = "View Zoom Ratio";
834   ot->idname = "CLIP_OT_view_zoom_ratio";
835   ot->description = "Set the zoom ratio (based on clip size)";
836
837   /* api callbacks */
838   ot->exec = view_zoom_ratio_exec;
839   ot->poll = ED_space_clip_view_clip_poll;
840
841   /* properties */
842   RNA_def_float(ot->srna,
843                 "ratio",
844                 0.0f,
845                 -FLT_MAX,
846                 FLT_MAX,
847                 "Ratio",
848                 "Zoom ratio, 1.0 is 1:1, higher is zoomed in, lower is zoomed out",
849                 -FLT_MAX,
850                 FLT_MAX);
851 }
852
853 /********************** view all operator *********************/
854
855 static int view_all_exec(bContext *C, wmOperator *op)
856 {
857   SpaceClip *sc;
858   ARegion *ar;
859   int w, h, width, height;
860   float aspx, aspy;
861   bool fit_view = RNA_boolean_get(op->ptr, "fit_view");
862   float zoomx, zoomy;
863
864   /* retrieve state */
865   sc = CTX_wm_space_clip(C);
866   ar = CTX_wm_region(C);
867
868   ED_space_clip_get_size(sc, &w, &h);
869   ED_space_clip_get_aspect(sc, &aspx, &aspy);
870
871   w = w * aspx;
872   h = h * aspy;
873
874   /* check if the image will fit in the image with zoom == 1 */
875   width = BLI_rcti_size_x(&ar->winrct) + 1;
876   height = BLI_rcti_size_y(&ar->winrct) + 1;
877
878   if (fit_view) {
879     const int margin = 5; /* margin from border */
880
881     zoomx = (float)width / (w + 2 * margin);
882     zoomy = (float)height / (h + 2 * margin);
883
884     sclip_zoom_set(C, min_ff(zoomx, zoomy), NULL);
885   }
886   else {
887     if ((w >= width || h >= height) && (width > 0 && height > 0)) {
888       zoomx = (float)width / w;
889       zoomy = (float)height / h;
890
891       /* find the zoom value that will fit the image in the image space */
892       sclip_zoom_set(C, 1.0f / power_of_2(1.0f / min_ff(zoomx, zoomy)), NULL);
893     }
894     else {
895       sclip_zoom_set(C, 1.0f, NULL);
896     }
897   }
898
899   sc->xof = sc->yof = 0.0f;
900
901   ED_region_tag_redraw(ar);
902
903   return OPERATOR_FINISHED;
904 }
905
906 void CLIP_OT_view_all(wmOperatorType *ot)
907 {
908   PropertyRNA *prop;
909
910   /* identifiers */
911   ot->name = "View All";
912   ot->idname = "CLIP_OT_view_all";
913   ot->description = "View whole image with markers";
914
915   /* api callbacks */
916   ot->exec = view_all_exec;
917   ot->poll = ED_space_clip_view_clip_poll;
918
919   /* properties */
920   prop = RNA_def_boolean(ot->srna, "fit_view", 0, "Fit View", "Fit frame to the viewport");
921   RNA_def_property_flag(prop, PROP_SKIP_SAVE);
922 }
923
924 /********************** view selected operator *********************/
925
926 static int view_selected_exec(bContext *C, wmOperator *UNUSED(op))
927 {
928   SpaceClip *sc = CTX_wm_space_clip(C);
929   ARegion *ar = CTX_wm_region(C);
930
931   sc->xlockof = 0.0f;
932   sc->ylockof = 0.0f;
933
934   ED_clip_view_selection(C, ar, 1);
935   ED_region_tag_redraw(ar);
936
937   return OPERATOR_FINISHED;
938 }
939
940 void CLIP_OT_view_selected(wmOperatorType *ot)
941 {
942   /* identifiers */
943   ot->name = "View Selected";
944   ot->idname = "CLIP_OT_view_selected";
945   ot->description = "View all selected elements";
946
947   /* api callbacks */
948   ot->exec = view_selected_exec;
949   ot->poll = ED_space_clip_view_clip_poll;
950 }
951
952 /********************** change frame operator *********************/
953
954 static bool change_frame_poll(bContext *C)
955 {
956   /* prevent changes during render */
957   if (G.is_rendering) {
958     return 0;
959   }
960
961   return ED_space_clip_poll(C);
962 }
963
964 static void change_frame_apply(bContext *C, wmOperator *op)
965 {
966   Scene *scene = CTX_data_scene(C);
967
968   /* set the new frame number */
969   CFRA = RNA_int_get(op->ptr, "frame");
970   FRAMENUMBER_MIN_CLAMP(CFRA);
971   SUBFRA = 0.0f;
972
973   /* do updates */
974   BKE_sound_seek_scene(CTX_data_main(C), scene);
975   WM_event_add_notifier(C, NC_SCENE | ND_FRAME, scene);
976 }
977
978 static int change_frame_exec(bContext *C, wmOperator *op)
979 {
980   change_frame_apply(C, op);
981
982   return OPERATOR_FINISHED;
983 }
984
985 static int frame_from_event(bContext *C, const wmEvent *event)
986 {
987   ARegion *ar = CTX_wm_region(C);
988   Scene *scene = CTX_data_scene(C);
989   int framenr = 0;
990
991   if (ar->regiontype == RGN_TYPE_WINDOW) {
992     float sfra = SFRA, efra = EFRA, framelen = ar->winx / (efra - sfra + 1);
993
994     framenr = sfra + event->mval[0] / framelen;
995   }
996   else {
997     float viewx, viewy;
998
999     UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &viewx, &viewy);
1000
1001     framenr = round_fl_to_int(viewx);
1002   }
1003
1004   return framenr;
1005 }
1006
1007 static int change_frame_invoke(bContext *C, wmOperator *op, const wmEvent *event)
1008 {
1009   ARegion *ar = CTX_wm_region(C);
1010
1011   if (ar->regiontype == RGN_TYPE_WINDOW) {
1012     if (event->mval[1] > 16) {
1013       return OPERATOR_PASS_THROUGH;
1014     }
1015   }
1016
1017   RNA_int_set(op->ptr, "frame", frame_from_event(C, event));
1018
1019   change_frame_apply(C, op);
1020
1021   /* add temp handler */
1022   WM_event_add_modal_handler(C, op);
1023
1024   return OPERATOR_RUNNING_MODAL;
1025 }
1026
1027 static int change_frame_modal(bContext *C, wmOperator *op, const wmEvent *event)
1028 {
1029   switch (event->type) {
1030     case ESCKEY:
1031       return OPERATOR_FINISHED;
1032
1033     case MOUSEMOVE:
1034       RNA_int_set(op->ptr, "frame", frame_from_event(C, event));
1035       change_frame_apply(C, op);
1036       break;
1037
1038     case LEFTMOUSE:
1039     case RIGHTMOUSE:
1040       if (event->val == KM_RELEASE) {
1041         return OPERATOR_FINISHED;
1042       }
1043       break;
1044   }
1045
1046   return OPERATOR_RUNNING_MODAL;
1047 }
1048
1049 void CLIP_OT_change_frame(wmOperatorType *ot)
1050 {
1051   /* identifiers */
1052   ot->name = "Change Frame";
1053   ot->idname = "CLIP_OT_change_frame";
1054   ot->description = "Interactively change the current frame number";
1055
1056   /* api callbacks */
1057   ot->exec = change_frame_exec;
1058   ot->invoke = change_frame_invoke;
1059   ot->modal = change_frame_modal;
1060   ot->poll = change_frame_poll;
1061
1062   /* flags */
1063   ot->flag = OPTYPE_BLOCKING | OPTYPE_UNDO;
1064
1065   /* rna */
1066   RNA_def_int(ot->srna, "frame", 0, MINAFRAME, MAXFRAME, "Frame", "", MINAFRAME, MAXFRAME);
1067 }
1068
1069 /********************** rebuild proxies operator *********************/
1070
1071 typedef struct ProxyBuildJob {
1072   Scene *scene;
1073   struct Main *main;
1074   MovieClip *clip;
1075   int clip_flag;
1076   bool stop;
1077   struct IndexBuildContext *index_context;
1078 } ProxyJob;
1079
1080 static void proxy_freejob(void *pjv)
1081 {
1082   ProxyJob *pj = pjv;
1083
1084   MEM_freeN(pj);
1085 }
1086
1087 static int proxy_bitflag_to_array(int size_flag, int build_sizes[4], int undistort)
1088 {
1089   int build_count = 0;
1090   int size_flags[2][4] = {
1091       {MCLIP_PROXY_SIZE_25, MCLIP_PROXY_SIZE_50, MCLIP_PROXY_SIZE_75, MCLIP_PROXY_SIZE_100},
1092       {MCLIP_PROXY_UNDISTORTED_SIZE_25,
1093        MCLIP_PROXY_UNDISTORTED_SIZE_50,
1094        MCLIP_PROXY_UNDISTORTED_SIZE_75,
1095        MCLIP_PROXY_UNDISTORTED_SIZE_100}};
1096   int size_nr = undistort ? 1 : 0;
1097
1098   if (size_flag & size_flags[size_nr][0]) {
1099     build_sizes[build_count++] = MCLIP_PROXY_RENDER_SIZE_25;
1100   }
1101
1102   if (size_flag & size_flags[size_nr][1]) {
1103     build_sizes[build_count++] = MCLIP_PROXY_RENDER_SIZE_50;
1104   }
1105
1106   if (size_flag & size_flags[size_nr][2]) {
1107     build_sizes[build_count++] = MCLIP_PROXY_RENDER_SIZE_75;
1108   }
1109
1110   if (size_flag & size_flags[size_nr][3]) {
1111     build_sizes[build_count++] = MCLIP_PROXY_RENDER_SIZE_100;
1112   }
1113
1114   return build_count;
1115 }
1116
1117 /* simple case for movies -- handle frame-by-frame, do threading within single frame */
1118 static void do_movie_proxy(void *pjv,
1119                            int *UNUSED(build_sizes),
1120                            int UNUSED(build_count),
1121                            int *build_undistort_sizes,
1122                            int build_undistort_count,
1123                            short *stop,
1124                            short *do_update,
1125                            float *progress)
1126 {
1127   ProxyJob *pj = pjv;
1128   Scene *scene = pj->scene;
1129   MovieClip *clip = pj->clip;
1130   struct MovieDistortion *distortion = NULL;
1131   int cfra, sfra = SFRA, efra = EFRA;
1132
1133   if (pj->index_context) {
1134     IMB_anim_index_rebuild(pj->index_context, stop, do_update, progress);
1135   }
1136
1137   if (!build_undistort_count) {
1138     if (*stop) {
1139       pj->stop = 1;
1140     }
1141
1142     return;
1143   }
1144   else {
1145     sfra = 1;
1146     efra = clip->len;
1147   }
1148
1149   if (build_undistort_count) {
1150     int threads = BLI_system_thread_count();
1151     int width, height;
1152
1153     BKE_movieclip_get_size(clip, NULL, &width, &height);
1154
1155     distortion = BKE_tracking_distortion_new(&clip->tracking, width, height);
1156     BKE_tracking_distortion_set_threads(distortion, threads);
1157   }
1158
1159   for (cfra = sfra; cfra <= efra; cfra++) {
1160     BKE_movieclip_build_proxy_frame(
1161         clip, pj->clip_flag, distortion, cfra, build_undistort_sizes, build_undistort_count, 1);
1162
1163     if (*stop || G.is_break) {
1164       break;
1165     }
1166
1167     *do_update = true;
1168     *progress = ((float)cfra - sfra) / (efra - sfra);
1169   }
1170
1171   if (distortion) {
1172     BKE_tracking_distortion_free(distortion);
1173   }
1174
1175   if (*stop) {
1176     pj->stop = 1;
1177   }
1178 }
1179
1180 /* *****
1181  * special case for sequences -- handle different frames in different threads,
1182  * loading from disk happens in critical section, decoding frame happens from
1183  * thread for maximal speed
1184  */
1185
1186 typedef struct ProxyQueue {
1187   int cfra;
1188   int sfra;
1189   int efra;
1190   SpinLock spin;
1191
1192   const short *stop;
1193   short *do_update;
1194   float *progress;
1195 } ProxyQueue;
1196
1197 typedef struct ProxyThread {
1198   MovieClip *clip;
1199   struct MovieDistortion *distortion;
1200   int *build_sizes, build_count;
1201   int *build_undistort_sizes, build_undistort_count;
1202 } ProxyThread;
1203
1204 static unsigned char *proxy_thread_next_frame(ProxyQueue *queue,
1205                                               MovieClip *clip,
1206                                               size_t *size_r,
1207                                               int *cfra_r)
1208 {
1209   unsigned char *mem = NULL;
1210
1211   BLI_spin_lock(&queue->spin);
1212   if (!*queue->stop && queue->cfra <= queue->efra) {
1213     MovieClipUser user = {0};
1214     char name[FILE_MAX];
1215     size_t size;
1216     int file;
1217
1218     user.framenr = queue->cfra;
1219
1220     BKE_movieclip_filename_for_frame(clip, &user, name);
1221
1222     file = BLI_open(name, O_BINARY | O_RDONLY, 0);
1223     if (file < 0) {
1224       BLI_spin_unlock(&queue->spin);
1225       return NULL;
1226     }
1227
1228     size = BLI_file_descriptor_size(file);
1229     if (size < 1) {
1230       close(file);
1231       BLI_spin_unlock(&queue->spin);
1232       return NULL;
1233     }
1234
1235     mem = MEM_mallocN(size, "movieclip proxy memory file");
1236
1237     if (read(file, mem, size) != size) {
1238       close(file);
1239       BLI_spin_unlock(&queue->spin);
1240       MEM_freeN(mem);
1241       return NULL;
1242     }
1243
1244     *size_r = size;
1245     *cfra_r = queue->cfra;
1246
1247     queue->cfra++;
1248     close(file);
1249
1250     *queue->do_update = 1;
1251     *queue->progress = (float)(queue->cfra - queue->sfra) / (queue->efra - queue->sfra);
1252   }
1253   BLI_spin_unlock(&queue->spin);
1254
1255   return mem;
1256 }
1257
1258 static void proxy_task_func(TaskPool *__restrict pool, void *task_data, int UNUSED(threadid))
1259 {
1260   ProxyThread *data = (ProxyThread *)task_data;
1261   ProxyQueue *queue = (ProxyQueue *)BLI_task_pool_userdata(pool);
1262   unsigned char *mem;
1263   size_t size;
1264   int cfra;
1265
1266   while ((mem = proxy_thread_next_frame(queue, data->clip, &size, &cfra))) {
1267     ImBuf *ibuf;
1268
1269     ibuf = IMB_ibImageFromMemory(mem,
1270                                  size,
1271                                  IB_rect | IB_multilayer | IB_alphamode_detect,
1272                                  data->clip->colorspace_settings.name,
1273                                  "proxy frame");
1274
1275     BKE_movieclip_build_proxy_frame_for_ibuf(
1276         data->clip, ibuf, NULL, cfra, data->build_sizes, data->build_count, false);
1277
1278     BKE_movieclip_build_proxy_frame_for_ibuf(data->clip,
1279                                              ibuf,
1280                                              data->distortion,
1281                                              cfra,
1282                                              data->build_undistort_sizes,
1283                                              data->build_undistort_count,
1284                                              true);
1285
1286     IMB_freeImBuf(ibuf);
1287
1288     MEM_freeN(mem);
1289   }
1290 }
1291
1292 static void do_sequence_proxy(void *pjv,
1293                               int *build_sizes,
1294                               int build_count,
1295                               int *build_undistort_sizes,
1296                               int build_undistort_count,
1297                               short *stop,
1298                               short *do_update,
1299                               float *progress)
1300 {
1301   ProxyJob *pj = pjv;
1302   MovieClip *clip = pj->clip;
1303   Scene *scene = pj->scene;
1304   TaskScheduler *task_scheduler = BLI_task_scheduler_get();
1305   TaskPool *task_pool;
1306   int sfra = SFRA, efra = EFRA;
1307   ProxyThread *handles;
1308   int i, tot_thread = BLI_task_scheduler_num_threads(task_scheduler);
1309   int width, height;
1310   ProxyQueue queue;
1311
1312   if (build_undistort_count) {
1313     BKE_movieclip_get_size(clip, NULL, &width, &height);
1314   }
1315
1316   BLI_spin_init(&queue.spin);
1317
1318   queue.cfra = sfra;
1319   queue.sfra = sfra;
1320   queue.efra = efra;
1321   queue.stop = stop;
1322   queue.do_update = do_update;
1323   queue.progress = progress;
1324
1325   task_pool = BLI_task_pool_create(task_scheduler, &queue);
1326   handles = MEM_callocN(sizeof(ProxyThread) * tot_thread, "proxy threaded handles");
1327   for (i = 0; i < tot_thread; i++) {
1328     ProxyThread *handle = &handles[i];
1329
1330     handle->clip = clip;
1331
1332     handle->build_count = build_count;
1333     handle->build_sizes = build_sizes;
1334
1335     handle->build_undistort_count = build_undistort_count;
1336     handle->build_undistort_sizes = build_undistort_sizes;
1337
1338     if (build_undistort_count) {
1339       handle->distortion = BKE_tracking_distortion_new(&clip->tracking, width, height);
1340     }
1341
1342     BLI_task_pool_push(task_pool, proxy_task_func, handle, false, TASK_PRIORITY_LOW);
1343   }
1344
1345   BLI_task_pool_work_and_wait(task_pool);
1346   BLI_task_pool_free(task_pool);
1347
1348   if (build_undistort_count) {
1349     for (i = 0; i < tot_thread; i++) {
1350       ProxyThread *handle = &handles[i];
1351       BKE_tracking_distortion_free(handle->distortion);
1352     }
1353   }
1354
1355   BLI_spin_end(&queue.spin);
1356   MEM_freeN(handles);
1357 }
1358
1359 static void proxy_startjob(void *pjv, short *stop, short *do_update, float *progress)
1360 {
1361   ProxyJob *pj = pjv;
1362   MovieClip *clip = pj->clip;
1363
1364   short size_flag;
1365   int build_sizes[4], build_count = 0;
1366   int build_undistort_sizes[4], build_undistort_count = 0;
1367
1368   size_flag = clip->proxy.build_size_flag;
1369
1370   build_count = proxy_bitflag_to_array(size_flag, build_sizes, 0);
1371   build_undistort_count = proxy_bitflag_to_array(size_flag, build_undistort_sizes, 1);
1372
1373   if (clip->source == MCLIP_SRC_MOVIE) {
1374     do_movie_proxy(pjv,
1375                    build_sizes,
1376                    build_count,
1377                    build_undistort_sizes,
1378                    build_undistort_count,
1379                    stop,
1380                    do_update,
1381                    progress);
1382   }
1383   else {
1384     do_sequence_proxy(pjv,
1385                       build_sizes,
1386                       build_count,
1387                       build_undistort_sizes,
1388                       build_undistort_count,
1389                       stop,
1390                       do_update,
1391                       progress);
1392   }
1393 }
1394
1395 static void proxy_endjob(void *pjv)
1396 {
1397   ProxyJob *pj = pjv;
1398
1399   if (pj->clip->anim) {
1400     IMB_close_anim_proxies(pj->clip->anim);
1401   }
1402
1403   if (pj->index_context) {
1404     IMB_anim_index_rebuild_finish(pj->index_context, pj->stop);
1405   }
1406
1407   if (pj->clip->source == MCLIP_SRC_MOVIE) {
1408     /* Timecode might have changed, so do a full reload to deal with this. */
1409     BKE_movieclip_reload(pj->main, pj->clip);
1410   }
1411   else {
1412     /* For image sequences we'll preserve original cache. */
1413     BKE_movieclip_clear_proxy_cache(pj->clip);
1414   }
1415
1416   WM_main_add_notifier(NC_MOVIECLIP | ND_DISPLAY, pj->clip);
1417 }
1418
1419 static int clip_rebuild_proxy_exec(bContext *C, wmOperator *UNUSED(op))
1420 {
1421   wmJob *wm_job;
1422   ProxyJob *pj;
1423   Scene *scene = CTX_data_scene(C);
1424   ScrArea *sa = CTX_wm_area(C);
1425   SpaceClip *sc = CTX_wm_space_clip(C);
1426   MovieClip *clip = ED_space_clip_get_clip(sc);
1427
1428   if ((clip->flag & MCLIP_USE_PROXY) == 0) {
1429     return OPERATOR_CANCELLED;
1430   }
1431
1432   wm_job = WM_jobs_get(CTX_wm_manager(C),
1433                        CTX_wm_window(C),
1434                        sa,
1435                        "Building Proxies",
1436                        WM_JOB_PROGRESS,
1437                        WM_JOB_TYPE_CLIP_BUILD_PROXY);
1438
1439   pj = MEM_callocN(sizeof(ProxyJob), "proxy rebuild job");
1440   pj->scene = scene;
1441   pj->main = CTX_data_main(C);
1442   pj->clip = clip;
1443   pj->clip_flag = clip->flag & MCLIP_TIMECODE_FLAGS;
1444
1445   if (clip->anim) {
1446     pj->index_context = IMB_anim_index_rebuild_context(clip->anim,
1447                                                        clip->proxy.build_tc_flag,
1448                                                        clip->proxy.build_size_flag,
1449                                                        clip->proxy.quality,
1450                                                        true,
1451                                                        NULL);
1452   }
1453
1454   WM_jobs_customdata_set(wm_job, pj, proxy_freejob);
1455   WM_jobs_timer(wm_job, 0.2, NC_MOVIECLIP | ND_DISPLAY, 0);
1456   WM_jobs_callbacks(wm_job, proxy_startjob, NULL, NULL, proxy_endjob);
1457
1458   G.is_break = false;
1459   WM_jobs_start(CTX_wm_manager(C), wm_job);
1460
1461   ED_area_tag_redraw(sa);
1462
1463   return OPERATOR_FINISHED;
1464 }
1465
1466 void CLIP_OT_rebuild_proxy(wmOperatorType *ot)
1467 {
1468   /* identifiers */
1469   ot->name = "Rebuild Proxy and Timecode Indices";
1470   ot->idname = "CLIP_OT_rebuild_proxy";
1471   ot->description = "Rebuild all selected proxies and timecode indices in the background";
1472
1473   /* api callbacks */
1474   ot->exec = clip_rebuild_proxy_exec;
1475   ot->poll = ED_space_clip_poll;
1476
1477   /* flags */
1478   ot->flag = OPTYPE_REGISTER;
1479 }
1480
1481 /********************** mode set operator *********************/
1482
1483 static int mode_set_exec(bContext *C, wmOperator *op)
1484 {
1485   SpaceClip *sc = CTX_wm_space_clip(C);
1486   int mode = RNA_enum_get(op->ptr, "mode");
1487
1488   sc->mode = mode;
1489
1490   WM_event_add_notifier(C, NC_SPACE | ND_SPACE_CLIP, NULL);
1491
1492   return OPERATOR_FINISHED;
1493 }
1494
1495 void CLIP_OT_mode_set(wmOperatorType *ot)
1496 {
1497   /* identifiers */
1498   ot->name = "Set Clip Mode";
1499   ot->description = "Set the clip interaction mode";
1500   ot->idname = "CLIP_OT_mode_set";
1501
1502   /* api callbacks */
1503   ot->exec = mode_set_exec;
1504
1505   ot->poll = ED_space_clip_poll;
1506
1507   /* properties */
1508   RNA_def_enum(ot->srna, "mode", rna_enum_clip_editor_mode_items, SC_MODE_TRACKING, "Mode", "");
1509 }
1510
1511 #ifdef WITH_INPUT_NDOF
1512 /********************** NDOF operator *********************/
1513
1514 /* Combined pan/zoom from a 3D mouse device.
1515  * Z zooms, XY pans
1516  * "view" (not "paper") control -- user moves the viewpoint, not the image being viewed
1517  * that explains the negative signs in the code below
1518  */
1519
1520 static int clip_view_ndof_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *event)
1521 {
1522   if (event->type != NDOF_MOTION) {
1523     return OPERATOR_CANCELLED;
1524   }
1525   else {
1526     SpaceClip *sc = CTX_wm_space_clip(C);
1527     ARegion *ar = CTX_wm_region(C);
1528     float pan_vec[3];
1529
1530     const wmNDOFMotionData *ndof = event->customdata;
1531     const float speed = NDOF_PIXELS_PER_SECOND;
1532
1533     WM_event_ndof_pan_get(ndof, pan_vec, true);
1534
1535     mul_v2_fl(pan_vec, (speed * ndof->dt) / sc->zoom);
1536     pan_vec[2] *= -ndof->dt;
1537
1538     sclip_zoom_set_factor(C, 1.0f + pan_vec[2], NULL);
1539     sc->xof += pan_vec[0];
1540     sc->yof += pan_vec[1];
1541
1542     ED_region_tag_redraw(ar);
1543
1544     return OPERATOR_FINISHED;
1545   }
1546 }
1547
1548 void CLIP_OT_view_ndof(wmOperatorType *ot)
1549 {
1550   /* identifiers */
1551   ot->name = "NDOF Pan/Zoom";
1552   ot->idname = "CLIP_OT_view_ndof";
1553   ot->description = "Use a 3D mouse device to pan/zoom the view";
1554
1555   /* api callbacks */
1556   ot->invoke = clip_view_ndof_invoke;
1557   ot->poll = ED_space_clip_view_clip_poll;
1558 }
1559 #endif /* WITH_INPUT_NDOF */
1560
1561 /********************** Prefetch operator *********************/
1562
1563 static int clip_prefetch_modal(bContext *C, wmOperator *UNUSED(op), const wmEvent *event)
1564 {
1565   /* no running blender, remove handler and pass through */
1566   if (0 == WM_jobs_test(CTX_wm_manager(C), CTX_wm_area(C), WM_JOB_TYPE_CLIP_PREFETCH)) {
1567     return OPERATOR_FINISHED | OPERATOR_PASS_THROUGH;
1568   }
1569
1570   /* running render */
1571   switch (event->type) {
1572     case ESCKEY:
1573       return OPERATOR_RUNNING_MODAL;
1574   }
1575
1576   return OPERATOR_PASS_THROUGH;
1577 }
1578
1579 static int clip_prefetch_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(_event))
1580 {
1581   clip_start_prefetch_job(C);
1582
1583   /* add modal handler for ESC */
1584   WM_event_add_modal_handler(C, op);
1585
1586   return OPERATOR_RUNNING_MODAL;
1587 }
1588
1589 void CLIP_OT_prefetch(wmOperatorType *ot)
1590 {
1591   /* identifiers */
1592   ot->name = "Prefetch Frames";
1593   ot->idname = "CLIP_OT_prefetch";
1594   ot->description = "Prefetch frames from disk for faster playback/tracking";
1595
1596   /* api callbacks */
1597   ot->poll = ED_space_clip_view_clip_poll;
1598   ot->invoke = clip_prefetch_invoke;
1599   ot->modal = clip_prefetch_modal;
1600 }
1601
1602 /********************** Set scene frames *********************/
1603
1604 static int clip_set_scene_frames_exec(bContext *C, wmOperator *UNUSED(op))
1605 {
1606   MovieClip *clip = CTX_data_edit_movieclip(C);
1607   Scene *scene = CTX_data_scene(C);
1608   int clip_length;
1609
1610   if (ELEM(NULL, scene, clip)) {
1611     return OPERATOR_CANCELLED;
1612   }
1613
1614   clip_length = BKE_movieclip_get_duration(clip);
1615
1616   scene->r.sfra = clip->start_frame;
1617   scene->r.efra = scene->r.sfra + clip_length - 1;
1618
1619   scene->r.efra = max_ii(scene->r.sfra, scene->r.efra);
1620
1621   WM_event_add_notifier(C, NC_SCENE | ND_FRAME, scene);
1622
1623   return OPERATOR_FINISHED;
1624 }
1625
1626 void CLIP_OT_set_scene_frames(wmOperatorType *ot)
1627 {
1628   /* identifiers */
1629   ot->name = "Set Scene Frames";
1630   ot->idname = "CLIP_OT_set_scene_frames";
1631   ot->description = "Set scene's start and end frame to match clip's start frame and length";
1632
1633   /* api callbacks */
1634   ot->poll = ED_space_clip_view_clip_poll;
1635   ot->exec = clip_set_scene_frames_exec;
1636 }
1637
1638 /******************** set 3d cursor operator ********************/
1639
1640 static int clip_set_2d_cursor_exec(bContext *C, wmOperator *op)
1641 {
1642   SpaceClip *sclip = CTX_wm_space_clip(C);
1643   bool show_cursor = false;
1644
1645   show_cursor |= sclip->mode == SC_MODE_MASKEDIT;
1646   show_cursor |= sclip->around == V3D_AROUND_CURSOR;
1647
1648   if (!show_cursor) {
1649     return OPERATOR_CANCELLED;
1650   }
1651
1652   RNA_float_get_array(op->ptr, "location", sclip->cursor);
1653
1654   WM_event_add_notifier(C, NC_SPACE | ND_SPACE_CLIP, NULL);
1655
1656   return OPERATOR_FINISHED;
1657 }
1658
1659 static int clip_set_2d_cursor_invoke(bContext *C, wmOperator *op, const wmEvent *event)
1660 {
1661   ARegion *ar = CTX_wm_region(C);
1662   SpaceClip *sclip = CTX_wm_space_clip(C);
1663   float location[2];
1664
1665   ED_clip_mouse_pos(sclip, ar, event->mval, location);
1666   RNA_float_set_array(op->ptr, "location", location);
1667
1668   return clip_set_2d_cursor_exec(C, op);
1669 }
1670
1671 void CLIP_OT_cursor_set(wmOperatorType *ot)
1672 {
1673   /* identifiers */
1674   ot->name = "Set 2D Cursor";
1675   ot->description = "Set 2D cursor location";
1676   ot->idname = "CLIP_OT_cursor_set";
1677
1678   /* api callbacks */
1679   ot->exec = clip_set_2d_cursor_exec;
1680   ot->invoke = clip_set_2d_cursor_invoke;
1681   ot->poll = ED_space_clip_poll;
1682
1683   /* flags */
1684   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1685
1686   /* properties */
1687   RNA_def_float_vector(ot->srna,
1688                        "location",
1689                        2,
1690                        NULL,
1691                        -FLT_MAX,
1692                        FLT_MAX,
1693                        "Location",
1694                        "Cursor location in normalized clip coordinates",
1695                        -10.0f,
1696                        10.0f);
1697 }
1698
1699 /********************** macros *********************/
1700
1701 void ED_operatormacros_clip(void)
1702 {
1703   wmOperatorType *ot;
1704   wmOperatorTypeMacro *otmacro;
1705
1706   ot = WM_operatortype_append_macro("CLIP_OT_add_marker_move",
1707                                     "Add Marker and Move",
1708                                     "Add new marker and move it on movie",
1709                                     OPTYPE_UNDO | OPTYPE_REGISTER);
1710   WM_operatortype_macro_define(ot, "CLIP_OT_add_marker");
1711   otmacro = WM_operatortype_macro_define(ot, "TRANSFORM_OT_translate");
1712   RNA_struct_idprops_unset(otmacro->ptr, "release_confirm");
1713
1714   ot = WM_operatortype_append_macro(
1715       "CLIP_OT_add_marker_slide",
1716       "Add Marker and Slide",
1717       "Add new marker and slide it with mouse until mouse button release",
1718       OPTYPE_UNDO | OPTYPE_REGISTER);
1719   WM_operatortype_macro_define(ot, "CLIP_OT_add_marker");
1720   otmacro = WM_operatortype_macro_define(ot, "TRANSFORM_OT_translate");
1721   RNA_boolean_set(otmacro->ptr, "release_confirm", true);
1722 }