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