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