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