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