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