cf4e76a842708fbf0b94ff69909c8c1ea8fe0aec
[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
34 #include "MEM_guardedalloc.h"
35
36 #include "DNA_userdef_types.h"
37 #include "DNA_scene_types.h"    /* min/max frames */
38
39 #include "BLI_path_util.h"
40 #include "BLI_utildefines.h"
41 #include "BLI_math.h"
42 #include "BLI_rect.h"
43
44 #include "BLF_translation.h"
45
46 #include "BKE_context.h"
47 #include "BKE_global.h"
48 #include "BKE_report.h"
49 #include "BKE_main.h"
50 #include "BKE_library.h"
51 #include "BKE_movieclip.h"
52 #include "BKE_sound.h"
53 #include "BKE_tracking.h"
54
55 #include "WM_api.h"
56 #include "WM_types.h"
57
58 #include "IMB_imbuf_types.h"
59 #include "IMB_imbuf.h"
60
61 #include "ED_screen.h"
62 #include "ED_clip.h"
63
64 #include "UI_interface.h"
65
66 #include "RNA_access.h"
67 #include "RNA_define.h"
68 #include "RNA_enum_types.h"
69
70 #include "UI_view2d.h"
71
72 #include "clip_intern.h"        // own include
73
74 /******************** view navigation utilities *********************/
75
76 static void sclip_zoom_set(const bContext *C, float zoom, float location[2])
77 {
78         SpaceClip *sc = CTX_wm_space_clip(C);
79         ARegion *ar = CTX_wm_region(C);
80
81         float oldzoom = sc->zoom;
82         int width, height;
83
84         sc->zoom = zoom;
85
86         if (sc->zoom < 0.1f || sc->zoom > 4.0f) {
87                 /* check zoom limits */
88                 ED_space_clip_get_size(sc, &width, &height);
89
90                 width *= sc->zoom;
91                 height *= sc->zoom;
92
93                 if ((width < 4) && (height < 4))
94                         sc->zoom = oldzoom;
95                 else if (BLI_rcti_size_x(&ar->winrct) <= sc->zoom)
96                         sc->zoom = oldzoom;
97                 else if (BLI_rcti_size_y(&ar->winrct) <= sc->zoom)
98                         sc->zoom = oldzoom;
99         }
100
101         if ((U.uiflag & USER_ZOOM_TO_MOUSEPOS) && location) {
102                 ED_space_clip_get_size(sc, &width, &height);
103
104                 sc->xof += ((location[0] - 0.5f) * width - sc->xof) * (sc->zoom - oldzoom) / sc->zoom;
105                 sc->yof += ((location[1] - 0.5f) * height - sc->yof) * (sc->zoom - oldzoom) / sc->zoom;
106         }
107 }
108
109 static void sclip_zoom_set_factor(const bContext *C, float zoomfac, float location[2])
110 {
111         SpaceClip *sc = CTX_wm_space_clip(C);
112
113         sclip_zoom_set(C, sc->zoom * zoomfac, location);
114 }
115
116 static void sclip_zoom_set_factor_exec(bContext *C, wmEvent *event, float factor)
117 {
118         ARegion *ar = CTX_wm_region(C);
119
120         float location[2], *mpos = NULL;
121
122         if (event) {
123                 SpaceClip *sc = CTX_wm_space_clip(C);
124
125                 ED_clip_mouse_pos(sc, ar, event->mval, location);
126                 mpos = location;
127         }
128
129         sclip_zoom_set_factor(C, factor, mpos);
130
131         ED_region_tag_redraw(ar);
132 }
133
134 /******************** open clip operator ********************/
135
136 static void clip_filesel(bContext *C, wmOperator *op, const char *path)
137 {
138         RNA_string_set(op->ptr, "directory", path);
139
140         WM_event_add_fileselect(C, op);
141 }
142
143 static void open_init(bContext *C, wmOperator *op)
144 {
145         PropertyPointerRNA *pprop;
146
147         op->customdata = pprop = MEM_callocN(sizeof(PropertyPointerRNA), "OpenPropertyPointerRNA");
148         uiIDContextProperty(C, &pprop->ptr, &pprop->prop);
149 }
150
151 static int open_cancel(bContext *UNUSED(C), wmOperator *op)
152 {
153         MEM_freeN(op->customdata);
154         op->customdata = NULL;
155
156         return OPERATOR_CANCELLED;
157 }
158
159 static int open_exec(bContext *C, wmOperator *op)
160 {
161         SpaceClip *sc = CTX_wm_space_clip(C);
162         bScreen *screen = CTX_wm_screen(C);
163         PropertyPointerRNA *pprop;
164         PointerRNA idptr;
165         MovieClip *clip = NULL;
166         char str[FILE_MAX];
167
168         if (RNA_collection_length(op->ptr, "files")) {
169                 PointerRNA fileptr;
170                 PropertyRNA *prop;
171                 char dir_only[FILE_MAX], file_only[FILE_MAX];
172                 int relative = RNA_boolean_get(op->ptr, "relative_path");
173
174                 RNA_string_get(op->ptr, "directory", dir_only);
175                 if (relative)
176                         BLI_path_rel(dir_only, G.main->name);
177
178                 prop = RNA_struct_find_property(op->ptr, "files");
179                 RNA_property_collection_lookup_int(op->ptr, prop, 0, &fileptr);
180                 RNA_string_get(&fileptr, "name", file_only);
181
182                 BLI_join_dirfile(str, sizeof(str), dir_only, file_only);
183         }
184         else {
185                 BKE_report(op->reports, RPT_ERROR, "No files selected to be opened");
186
187                 return OPERATOR_CANCELLED;
188         }
189
190         /* default to frame 1 if there's no scene in context */
191
192         errno = 0;
193
194         clip = BKE_movieclip_file_add(str);
195
196         if (!clip) {
197                 if (op->customdata)
198                         MEM_freeN(op->customdata);
199
200                 BKE_reportf(op->reports, RPT_ERROR, "Cannot read '%s': %s", str,
201                             errno ? strerror(errno) : TIP_("unsupported movie clip format"));
202
203                 return OPERATOR_CANCELLED;
204         }
205
206         if (!op->customdata)
207                 open_init(C, op);
208
209         /* hook into UI */
210         pprop = op->customdata;
211
212         if (pprop->prop) {
213                 /* when creating new ID blocks, use is already 1, but RNA
214                  * pointer se also increases user, so this compensates it */
215                 clip->id.us--;
216
217                 RNA_id_pointer_create(&clip->id, &idptr);
218                 RNA_property_pointer_set(&pprop->ptr, pprop->prop, idptr);
219                 RNA_property_update(C, &pprop->ptr, pprop->prop);
220         }
221         else if (sc) {
222                 ED_space_clip_set_clip(C, screen, sc, clip);
223         }
224
225         WM_event_add_notifier(C, NC_MOVIECLIP | NA_ADDED, clip);
226
227         MEM_freeN(op->customdata);
228
229         return OPERATOR_FINISHED;
230 }
231
232 static int open_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(event))
233 {
234         SpaceClip *sc = CTX_wm_space_clip(C);
235         char path[FILE_MAX];
236         MovieClip *clip = NULL;
237
238         if (sc)
239                 clip = ED_space_clip_get_clip(sc);
240
241         if (clip) {
242                 strncpy(path, clip->name, sizeof(path));
243
244                 BLI_path_abs(path, G.main->name);
245                 BLI_parent_dir(path);
246         }
247         else {
248                 strncpy(path, U.textudir, sizeof(path));
249         }
250
251         if (RNA_struct_property_is_set(op->ptr, "files"))
252                 return open_exec(C, op);
253
254         if (!RNA_struct_property_is_set(op->ptr, "relative_path"))
255                 RNA_boolean_set(op->ptr, "relative_path", U.flag & USER_RELPATHS);
256
257         open_init(C, op);
258
259         clip_filesel(C, op, path);
260
261         return OPERATOR_RUNNING_MODAL;
262 }
263
264 void CLIP_OT_open(wmOperatorType *ot)
265 {
266         /* identifiers */
267         ot->name = "Open Clip";
268         ot->description = "Load a sequence of frames or a movie file";
269         ot->idname = "CLIP_OT_open";
270
271         /* api callbacks */
272         ot->exec = open_exec;
273         ot->invoke = open_invoke;
274         ot->cancel = open_cancel;
275
276         /* flags */
277         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
278
279         /* properties */
280         WM_operator_properties_filesel(ot, FOLDERFILE | IMAGEFILE | MOVIEFILE, FILE_SPECIAL, FILE_OPENFILE,
281                                        WM_FILESEL_RELPATH | WM_FILESEL_FILES | WM_FILESEL_DIRECTORY, FILE_DEFAULTDISPLAY);
282 }
283
284 /******************* reload clip operator *********************/
285
286 static int reload_exec(bContext *C, wmOperator *UNUSED(op))
287 {
288         MovieClip *clip = CTX_data_edit_movieclip(C);
289
290         if (!clip)
291                 return OPERATOR_CANCELLED;
292
293         BKE_movieclip_reload(clip);
294
295         WM_event_add_notifier(C, NC_MOVIECLIP | NA_EDITED, clip);
296
297         return OPERATOR_FINISHED;
298 }
299
300 void CLIP_OT_reload(wmOperatorType *ot)
301 {
302         /* identifiers */
303         ot->name = "Reload Clip";
304         ot->description = "Reload clip";
305         ot->idname = "CLIP_OT_reload";
306
307         /* api callbacks */
308         ot->exec = reload_exec;
309 }
310
311 /********************** view pan operator *********************/
312
313 typedef struct ViewPanData {
314         float x, y;
315         float xof, yof, xorig, yorig;
316         int event_type;
317         float *vec;
318 } ViewPanData;
319
320 static void view_pan_init(bContext *C, wmOperator *op, wmEvent *event)
321 {
322         SpaceClip *sc = CTX_wm_space_clip(C);
323         ViewPanData *vpd;
324
325         op->customdata = vpd = MEM_callocN(sizeof(ViewPanData), "ClipViewPanData");
326         WM_cursor_modal(CTX_wm_window(C), BC_NSEW_SCROLLCURSOR);
327
328         vpd->x = event->x;
329         vpd->y = event->y;
330
331         if (sc->flag & SC_LOCK_SELECTION)
332                 vpd->vec = &sc->xlockof;
333         else
334                 vpd->vec = &sc->xof;
335
336         copy_v2_v2(&vpd->xof, vpd->vec);
337         copy_v2_v2(&vpd->xorig, &vpd->xof);
338
339         vpd->event_type = event->type;
340
341         WM_event_add_modal_handler(C, op);
342 }
343
344 static void view_pan_exit(bContext *C, wmOperator *op, int cancel)
345 {
346         ViewPanData *vpd = op->customdata;
347
348         if (cancel) {
349                 copy_v2_v2(vpd->vec, &vpd->xorig);
350
351                 ED_region_tag_redraw(CTX_wm_region(C));
352         }
353
354         WM_cursor_restore(CTX_wm_window(C));
355         MEM_freeN(op->customdata);
356 }
357
358 static int view_pan_exec(bContext *C, wmOperator *op)
359 {
360         SpaceClip *sc = CTX_wm_space_clip(C);
361         float offset[2];
362
363         RNA_float_get_array(op->ptr, "offset", offset);
364
365         if (sc->flag & SC_LOCK_SELECTION) {
366                 sc->xlockof += offset[0];
367                 sc->ylockof += offset[1];
368         }
369         else {
370                 sc->xof += offset[0];
371                 sc->yof += offset[1];
372         }
373
374         ED_region_tag_redraw(CTX_wm_region(C));
375
376         return OPERATOR_FINISHED;
377 }
378
379 static int view_pan_invoke(bContext *C, wmOperator *op, wmEvent *event)
380 {
381         if (event->type == MOUSEPAN) {
382                 SpaceClip *sc = CTX_wm_space_clip(C);
383                 float offset[2];
384
385                 offset[0] = (event->prevx - event->x) / sc->zoom;
386                 offset[1] = (event->prevy - event->y) / sc->zoom;
387
388                 RNA_float_set_array(op->ptr, "offset", offset);
389
390                 view_pan_exec(C, op);
391
392                 return OPERATOR_FINISHED;
393         }
394         else {
395                 view_pan_init(C, op, event);
396
397                 return OPERATOR_RUNNING_MODAL;
398         }
399 }
400
401 static int view_pan_modal(bContext *C, wmOperator *op, wmEvent *event)
402 {
403         SpaceClip *sc = CTX_wm_space_clip(C);
404         ViewPanData *vpd = op->customdata;
405         float offset[2];
406
407         switch (event->type) {
408                 case MOUSEMOVE:
409                         copy_v2_v2(vpd->vec, &vpd->xorig);
410                         offset[0] = (vpd->x - event->x) / sc->zoom;
411                         offset[1] = (vpd->y - event->y) / sc->zoom;
412                         RNA_float_set_array(op->ptr, "offset", offset);
413                         view_pan_exec(C, op);
414                         break;
415                 case ESCKEY:
416                         view_pan_exit(C, op, 1);
417
418                         return OPERATOR_CANCELLED;
419                 case SPACEKEY:
420                         view_pan_exit(C, op, 0);
421
422                         return OPERATOR_FINISHED;
423                 default:
424                         if (event->type == vpd->event_type && event->val == KM_RELEASE) {
425                                 view_pan_exit(C, op, 0);
426
427                                 return OPERATOR_FINISHED;
428                         }
429                         break;
430         }
431
432         return OPERATOR_RUNNING_MODAL;
433 }
434
435 static int view_pan_cancel(bContext *C, wmOperator *op)
436 {
437         view_pan_exit(C, op, 1);
438
439         return OPERATOR_CANCELLED;
440 }
441
442 void CLIP_OT_view_pan(wmOperatorType *ot)
443 {
444         /* identifiers */
445         ot->name = "View Pan";
446         ot->idname = "CLIP_OT_view_pan";
447         ot->description = "Pan the view";
448
449         /* api callbacks */
450         ot->exec = view_pan_exec;
451         ot->invoke = view_pan_invoke;
452         ot->modal = view_pan_modal;
453         ot->cancel = view_pan_cancel;
454         ot->poll = ED_space_clip_view_clip_poll;
455
456         /* flags */
457         ot->flag = OPTYPE_BLOCKING;
458
459         /* properties */
460         RNA_def_float_vector(ot->srna, "offset", 2, NULL, -FLT_MAX, FLT_MAX,
461                              "Offset", "Offset in floating point units, 1.0 is the width and height of the image", -FLT_MAX, FLT_MAX);
462 }
463
464 /********************** view zoom operator *********************/
465
466 typedef struct ViewZoomData {
467         float x, y;
468         float zoom;
469         int event_type;
470         float location[2];
471 } ViewZoomData;
472
473 static void view_zoom_init(bContext *C, wmOperator *op, wmEvent *event)
474 {
475         SpaceClip *sc = CTX_wm_space_clip(C);
476         ARegion *ar = CTX_wm_region(C);
477
478         ViewZoomData *vpd;
479
480         op->customdata = vpd = MEM_callocN(sizeof(ViewZoomData), "ClipViewZoomData");
481         WM_cursor_modal(CTX_wm_window(C), BC_NSEW_SCROLLCURSOR);
482
483         vpd->x = event->x;
484         vpd->y = event->y;
485         vpd->zoom = sc->zoom;
486         vpd->event_type = event->type;
487
488         ED_clip_mouse_pos(sc, ar, event->mval, vpd->location);
489
490         WM_event_add_modal_handler(C, op);
491 }
492
493 static void view_zoom_exit(bContext *C, wmOperator *op, int cancel)
494 {
495         SpaceClip *sc = CTX_wm_space_clip(C);
496         ViewZoomData *vpd = op->customdata;
497
498         if (cancel) {
499                 sc->zoom = vpd->zoom;
500                 ED_region_tag_redraw(CTX_wm_region(C));
501         }
502
503         WM_cursor_restore(CTX_wm_window(C));
504         MEM_freeN(op->customdata);
505 }
506
507 static int view_zoom_exec(bContext *C, wmOperator *op)
508 {
509         sclip_zoom_set_factor(C, RNA_float_get(op->ptr, "factor"), NULL);
510
511         ED_region_tag_redraw(CTX_wm_region(C));
512
513         return OPERATOR_FINISHED;
514 }
515
516 static int view_zoom_invoke(bContext *C, wmOperator *op, wmEvent *event)
517 {
518         if (event->type == MOUSEZOOM || event->type == MOUSEPAN) {
519                 float delta, factor;
520
521                 delta = event->prevx - event->x + event->prevy - event->y;
522
523                 if (U.uiflag & USER_ZOOM_INVERT)
524                         delta *= -1;
525
526                 factor = 1.0f + delta / 300.0f;
527                 RNA_float_set(op->ptr, "factor", factor);
528
529                 sclip_zoom_set_factor_exec(C, event, factor);
530
531                 return OPERATOR_FINISHED;
532         }
533         else {
534                 view_zoom_init(C, op, event);
535
536                 return OPERATOR_RUNNING_MODAL;
537         }
538 }
539
540 static int view_zoom_modal(bContext *C, wmOperator *op, wmEvent *event)
541 {
542         ViewZoomData *vpd = op->customdata;
543         float delta, factor;
544
545         switch (event->type) {
546                 case MOUSEMOVE:
547                         delta = event->x - vpd->x + event->y - vpd->y;
548
549                         if (U.uiflag & USER_ZOOM_INVERT)
550                                 delta *= -1;
551
552                         factor = 1.0f + delta / 300.0f;
553                         RNA_float_set(op->ptr, "factor", factor);
554                         sclip_zoom_set(C, vpd->zoom * factor, vpd->location);
555                         ED_region_tag_redraw(CTX_wm_region(C));
556                         break;
557                 default:
558                         if (event->type == vpd->event_type && event->val == KM_RELEASE) {
559                                 view_zoom_exit(C, op, 0);
560
561                                 return OPERATOR_FINISHED;
562                         }
563                         break;
564         }
565
566         return OPERATOR_RUNNING_MODAL;
567 }
568
569 static int view_zoom_cancel(bContext *C, wmOperator *op)
570 {
571         view_zoom_exit(C, op, 1);
572
573         return OPERATOR_CANCELLED;
574 }
575
576 void CLIP_OT_view_zoom(wmOperatorType *ot)
577 {
578         /* identifiers */
579         ot->name = "View Zoom";
580         ot->idname = "CLIP_OT_view_zoom";
581         ot->description = "Zoom in/out the view";
582
583         /* api callbacks */
584         ot->exec = view_zoom_exec;
585         ot->invoke = view_zoom_invoke;
586         ot->modal = view_zoom_modal;
587         ot->cancel = view_zoom_cancel;
588         ot->poll = ED_space_clip_view_clip_poll;
589
590         /* flags */
591         ot->flag = OPTYPE_BLOCKING | OPTYPE_GRAB_POINTER;
592
593         /* properties */
594         RNA_def_float(ot->srna, "factor", 0.0f, -FLT_MAX, FLT_MAX,
595                       "Factor", "Zoom factor, values higher than 1.0 zoom in, lower values zoom out", -FLT_MAX, FLT_MAX);
596 }
597
598 /********************** view zoom in/out operator *********************/
599
600 static int view_zoom_in_exec(bContext *C, wmOperator *op)
601 {
602         float location[2];
603
604         RNA_float_get_array(op->ptr, "location", location);
605
606         sclip_zoom_set_factor(C, 1.25f, location);
607
608         ED_region_tag_redraw(CTX_wm_region(C));
609
610         return OPERATOR_FINISHED;
611 }
612
613 static int view_zoom_in_invoke(bContext *C, wmOperator *op, wmEvent *event)
614 {
615         SpaceClip *sc = CTX_wm_space_clip(C);
616         ARegion *ar = CTX_wm_region(C);
617
618         float location[2];
619
620         ED_clip_mouse_pos(sc, ar, event->mval, location);
621         RNA_float_set_array(op->ptr, "location", location);
622
623         return view_zoom_in_exec(C, op);
624 }
625
626 void CLIP_OT_view_zoom_in(wmOperatorType *ot)
627 {
628         /* identifiers */
629         ot->name = "View Zoom In";
630         ot->idname = "CLIP_OT_view_zoom_in";
631         ot->description = "Zoom in the view";
632
633         /* api callbacks */
634         ot->exec = view_zoom_in_exec;
635         ot->invoke = view_zoom_in_invoke;
636         ot->poll = ED_space_clip_view_clip_poll;
637
638         /* properties */
639         RNA_def_float_vector(ot->srna, "location", 2, NULL, -FLT_MAX, FLT_MAX, "Location",
640                              "Cursor location in screen coordinates", -10.0f, 10.0f);
641 }
642
643 static int view_zoom_out_exec(bContext *C, wmOperator *op)
644 {
645         float location[2];
646
647         RNA_float_get_array(op->ptr, "location", location);
648
649         sclip_zoom_set_factor(C, 0.8f, location);
650
651         ED_region_tag_redraw(CTX_wm_region(C));
652
653         return OPERATOR_FINISHED;
654 }
655
656 static int view_zoom_out_invoke(bContext *C, wmOperator *op, wmEvent *event)
657 {
658         SpaceClip *sc = CTX_wm_space_clip(C);
659         ARegion *ar = CTX_wm_region(C);
660
661         float location[2];
662
663         ED_clip_mouse_pos(sc, ar, event->mval, location);
664         RNA_float_set_array(op->ptr, "location", location);
665
666         return view_zoom_out_exec(C, op);
667 }
668
669 void CLIP_OT_view_zoom_out(wmOperatorType *ot)
670 {
671         /* identifiers */
672         ot->name = "View Zoom Out";
673         ot->idname = "CLIP_OT_view_zoom_out";
674         ot->description = "Zoom out the view";
675
676         /* api callbacks */
677         ot->exec = view_zoom_out_exec;
678         ot->invoke = view_zoom_out_invoke;
679         ot->poll = ED_space_clip_view_clip_poll;
680
681         /* properties */
682         RNA_def_float_vector(ot->srna, "location", 2, NULL, -FLT_MAX, FLT_MAX, "Location",
683                              "Cursor location in normalized (0.0-1.0) coordinates", -10.0f, 10.0f);
684 }
685
686 /********************** view zoom ratio operator *********************/
687
688 static int view_zoom_ratio_exec(bContext *C, wmOperator *op)
689 {
690         SpaceClip *sc = CTX_wm_space_clip(C);
691
692         sclip_zoom_set(C, RNA_float_get(op->ptr, "ratio"), NULL);
693
694         /* ensure pixel exact locations for draw */
695         sc->xof = (int) sc->xof;
696         sc->yof = (int) sc->yof;
697
698         ED_region_tag_redraw(CTX_wm_region(C));
699
700         return OPERATOR_FINISHED;
701 }
702
703 void CLIP_OT_view_zoom_ratio(wmOperatorType *ot)
704 {
705         /* identifiers */
706         ot->name = "View Zoom Ratio";
707         ot->idname = "CLIP_OT_view_zoom_ratio";
708         ot->description = "Set the zoom ratio (based on clip size)";
709
710         /* api callbacks */
711         ot->exec = view_zoom_ratio_exec;
712         ot->poll = ED_space_clip_view_clip_poll;
713
714         /* properties */
715         RNA_def_float(ot->srna, "ratio", 0.0f, -FLT_MAX, FLT_MAX,
716                       "Ratio", "Zoom ratio, 1.0 is 1:1, higher is zoomed in, lower is zoomed out", -FLT_MAX, FLT_MAX);
717 }
718
719 /********************** view all operator *********************/
720
721 static int view_all_exec(bContext *C, wmOperator *op)
722 {
723         SpaceClip *sc;
724         ARegion *ar;
725         int w, h, width, height;
726         float aspx, aspy;
727         int fit_view = RNA_boolean_get(op->ptr, "fit_view");
728         float zoomx, zoomy;
729
730         /* retrieve state */
731         sc = CTX_wm_space_clip(C);
732         ar = CTX_wm_region(C);
733
734         ED_space_clip_get_size(sc, &w, &h);
735         ED_space_clip_get_aspect(sc, &aspx, &aspy);
736
737         w = w * aspx;
738         h = h * aspy;
739
740         /* check if the image will fit in the image with zoom == 1 */
741         width  = BLI_rcti_size_x(&ar->winrct) + 1;
742         height = BLI_rcti_size_y(&ar->winrct) + 1;
743
744         if (fit_view) {
745                 const int margin = 5; /* margin from border */
746
747                 zoomx = (float) width / (w + 2 * margin);
748                 zoomy = (float) height / (h + 2 * margin);
749
750                 sclip_zoom_set(C, min_ff(zoomx, zoomy), NULL);
751         }
752         else {
753                 if ((w >= width || h >= height) && (width > 0 && height > 0)) {
754                         zoomx = (float) width / w;
755                         zoomy = (float) height / h;
756
757                         /* find the zoom value that will fit the image in the image space */
758                         sclip_zoom_set(C, 1.0f / power_of_2(1.0f / min_ff(zoomx, zoomy)), NULL);
759                 }
760                 else
761                         sclip_zoom_set(C, 1.0f, NULL);
762         }
763
764         sc->xof = sc->yof = 0.0f;
765
766         ED_region_tag_redraw(CTX_wm_region(C));
767
768         return OPERATOR_FINISHED;
769 }
770
771 void CLIP_OT_view_all(wmOperatorType *ot)
772 {
773         /* identifiers */
774         ot->name = "View All";
775         ot->idname = "CLIP_OT_view_all";
776         ot->description = "View whole image with markers";
777
778         /* api callbacks */
779         ot->exec = view_all_exec;
780         ot->poll = ED_space_clip_view_clip_poll;
781
782         /* properties */
783         RNA_def_boolean(ot->srna, "fit_view", 0, "Fit View", "Fit frame to the viewport");
784 }
785
786 /********************** view selected operator *********************/
787
788 static int view_selected_exec(bContext *C, wmOperator *UNUSED(op))
789 {
790         SpaceClip *sc = CTX_wm_space_clip(C);
791         ARegion *ar = CTX_wm_region(C);
792
793         sc->xlockof = 0.0f;
794         sc->ylockof = 0.0f;
795
796         ED_clip_view_selection(C, ar, 1);
797         ED_region_tag_redraw(CTX_wm_region(C));
798
799         return OPERATOR_FINISHED;
800 }
801
802 void CLIP_OT_view_selected(wmOperatorType *ot)
803 {
804         /* identifiers */
805         ot->name = "View Selected";
806         ot->idname = "CLIP_OT_view_selected";
807         ot->description = "View all selected elements";
808
809         /* api callbacks */
810         ot->exec = view_selected_exec;
811         ot->poll = ED_space_clip_view_clip_poll;
812 }
813
814 /********************** change frame operator *********************/
815
816 static int change_frame_poll(bContext *C)
817 {
818         /* prevent changes during render */
819         if (G.is_rendering)
820                 return 0;
821
822         return ED_space_clip_poll(C);
823 }
824
825 static void change_frame_apply(bContext *C, wmOperator *op)
826 {
827         Scene *scene = CTX_data_scene(C);
828
829         /* set the new frame number */
830         CFRA = RNA_int_get(op->ptr, "frame");
831         FRAMENUMBER_MIN_CLAMP(CFRA);
832         SUBFRA = 0.0f;
833
834         /* do updates */
835         sound_seek_scene(CTX_data_main(C), CTX_data_scene(C));
836         WM_event_add_notifier(C, NC_SCENE | ND_FRAME, scene);
837 }
838
839 static int change_frame_exec(bContext *C, wmOperator *op)
840 {
841         change_frame_apply(C, op);
842
843         return OPERATOR_FINISHED;
844 }
845
846 static int frame_from_event(bContext *C, wmEvent *event)
847 {
848         ARegion *ar = CTX_wm_region(C);
849         Scene *scene = CTX_data_scene(C);
850         int framenr = 0;
851
852         if (ar->regiontype == RGN_TYPE_WINDOW) {
853                 float sfra = SFRA, efra = EFRA, framelen = ar->winx / (efra - sfra + 1);
854
855                 framenr = sfra + event->mval[0] / framelen;
856         }
857         else {
858                 float viewx, viewy;
859
860                 UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &viewx, &viewy);
861
862                 framenr = (int) floor(viewx + 0.5f);
863         }
864
865         return framenr;
866 }
867
868 static int change_frame_invoke(bContext *C, wmOperator *op, wmEvent *event)
869 {
870         ARegion *ar = CTX_wm_region(C);
871
872         if (ar->regiontype == RGN_TYPE_WINDOW) {
873                 if (event->mval[1] > 16)
874                         return OPERATOR_PASS_THROUGH;
875         }
876
877         RNA_int_set(op->ptr, "frame", frame_from_event(C, event));
878
879         change_frame_apply(C, op);
880
881         /* add temp handler */
882         WM_event_add_modal_handler(C, op);
883
884         return OPERATOR_RUNNING_MODAL;
885 }
886
887 static int change_frame_modal(bContext *C, wmOperator *op, wmEvent *event)
888 {
889         switch (event->type) {
890                 case ESCKEY:
891                         return OPERATOR_FINISHED;
892
893                 case MOUSEMOVE:
894                         RNA_int_set(op->ptr, "frame", frame_from_event(C, event));
895                         change_frame_apply(C, op);
896                         break;
897
898                 case LEFTMOUSE:
899                 case RIGHTMOUSE:
900                         if (event->val == KM_RELEASE)
901                                 return OPERATOR_FINISHED;
902                         break;
903         }
904
905         return OPERATOR_RUNNING_MODAL;
906 }
907
908 void CLIP_OT_change_frame(wmOperatorType *ot)
909 {
910         /* identifiers */
911         ot->name = "Change Frame";
912         ot->idname = "CLIP_OT_change_frame";
913         ot->description = "Interactively change the current frame number";
914
915         /* api callbacks */
916         ot->exec = change_frame_exec;
917         ot->invoke = change_frame_invoke;
918         ot->modal = change_frame_modal;
919         ot->poll = change_frame_poll;
920
921         /* flags */
922         ot->flag = OPTYPE_BLOCKING | OPTYPE_UNDO;
923
924         /* rna */
925         RNA_def_int(ot->srna, "frame", 0, MINAFRAME, MAXFRAME, "Frame", "", MINAFRAME, MAXFRAME);
926 }
927
928 /********************** rebuild proxies operator *********************/
929
930 typedef struct ProxyBuildJob {
931         Scene *scene;
932         struct Main *main;
933         MovieClip *clip;
934         int clip_flag, stop;
935         struct IndexBuildContext *index_context;
936 } ProxyJob;
937
938 static void proxy_freejob(void *pjv)
939 {
940         ProxyJob *pj = pjv;
941
942         MEM_freeN(pj);
943 }
944
945 static int proxy_bitflag_to_array(int size_flag, int build_sizes[4], int undistort)
946 {
947         int build_count = 0;
948         int size_flags[2][4] = {{MCLIP_PROXY_SIZE_25,
949                                  MCLIP_PROXY_SIZE_50,
950                                  MCLIP_PROXY_SIZE_75,
951                                  MCLIP_PROXY_SIZE_100},
952                                 {MCLIP_PROXY_UNDISTORTED_SIZE_25,
953                                  MCLIP_PROXY_UNDISTORTED_SIZE_50,
954                                  MCLIP_PROXY_UNDISTORTED_SIZE_75,
955                                  MCLIP_PROXY_UNDISTORTED_SIZE_100}};
956         int size_nr = undistort ? 1 : 0;
957
958         if (size_flag & size_flags[size_nr][0])
959                 build_sizes[build_count++] = MCLIP_PROXY_RENDER_SIZE_25;
960
961         if (size_flag & size_flags[size_nr][1])
962                 build_sizes[build_count++] = MCLIP_PROXY_RENDER_SIZE_50;
963
964         if (size_flag & size_flags[size_nr][2])
965                 build_sizes[build_count++] = MCLIP_PROXY_RENDER_SIZE_75;
966
967         if (size_flag & size_flags[size_nr][3])
968                 build_sizes[build_count++] = MCLIP_PROXY_RENDER_SIZE_100;
969
970         return build_count;
971 }
972
973 /* only this runs inside thread */
974 static void proxy_startjob(void *pjv, short *stop, short *do_update, float *progress)
975 {
976         ProxyJob *pj = pjv;
977         Scene *scene = pj->scene;
978         MovieClip *clip = pj->clip;
979         struct MovieDistortion *distortion = NULL;
980         short size_flag;
981         int cfra, sfra = SFRA, efra = EFRA;
982         int build_sizes[4], build_count = 0;
983         int build_undistort_sizes[4], build_undistort_count = 0;
984
985         size_flag = clip->proxy.build_size_flag;
986
987         build_count = proxy_bitflag_to_array(size_flag, build_sizes, 0);
988         build_undistort_count = proxy_bitflag_to_array(size_flag, build_undistort_sizes, 1);
989
990         if (clip->source == MCLIP_SRC_MOVIE) {
991                 if (pj->index_context)
992                         IMB_anim_index_rebuild(pj->index_context, stop, do_update, progress);
993
994                 if (!build_undistort_count) {
995                         if (*stop)
996                                 pj->stop = 1;
997
998                         return;
999                 }
1000                 else {
1001                         sfra = 1;
1002                         efra = IMB_anim_get_duration(clip->anim, IMB_TC_NONE);
1003                 }
1004         }
1005
1006         if (build_undistort_count)
1007                 distortion = BKE_tracking_distortion_new();
1008
1009         for (cfra = sfra; cfra <= efra; cfra++) {
1010                 if (clip->source != MCLIP_SRC_MOVIE)
1011                         BKE_movieclip_build_proxy_frame(clip, pj->clip_flag, NULL, cfra, build_sizes, build_count, 0);
1012
1013                 BKE_movieclip_build_proxy_frame(clip, pj->clip_flag, distortion, cfra,
1014                                                 build_undistort_sizes, build_undistort_count, 1);
1015
1016                 if (*stop || G.is_break)
1017                         break;
1018
1019                 *do_update = TRUE;
1020                 *progress = ((float) cfra - sfra) / (efra - sfra);
1021         }
1022
1023         if (distortion)
1024                 BKE_tracking_distortion_free(distortion);
1025
1026         if (*stop)
1027                 pj->stop = 1;
1028 }
1029
1030 static void proxy_endjob(void *pjv)
1031 {
1032         ProxyJob *pj = pjv;
1033
1034         if (pj->clip->anim)
1035                 IMB_close_anim_proxies(pj->clip->anim);
1036
1037         if (pj->index_context)
1038                 IMB_anim_index_rebuild_finish(pj->index_context, pj->stop);
1039
1040         BKE_movieclip_reload(pj->clip);
1041
1042         WM_main_add_notifier(NC_MOVIECLIP | ND_DISPLAY, pj->clip);
1043 }
1044
1045 static int clip_rebuild_proxy_exec(bContext *C, wmOperator *UNUSED(op))
1046 {
1047         wmJob *wm_job;
1048         ProxyJob *pj;
1049         Scene *scene = CTX_data_scene(C);
1050         ScrArea *sa = CTX_wm_area(C);
1051         SpaceClip *sc = CTX_wm_space_clip(C);
1052         MovieClip *clip = ED_space_clip_get_clip(sc);
1053
1054         if ((clip->flag & MCLIP_USE_PROXY) == 0)
1055                 return OPERATOR_CANCELLED;
1056
1057         wm_job = WM_jobs_get(CTX_wm_manager(C), CTX_wm_window(C), sa, "Building Proxies",
1058                              WM_JOB_PROGRESS, WM_JOB_TYPE_CLIP_BUILD_PROXY);
1059
1060         pj = MEM_callocN(sizeof(ProxyJob), "proxy rebuild job");
1061         pj->scene = scene;
1062         pj->main = CTX_data_main(C);
1063         pj->clip = clip;
1064         pj->clip_flag = clip->flag & MCLIP_TIMECODE_FLAGS;
1065
1066         if (clip->anim) {
1067                 pj->index_context = IMB_anim_index_rebuild_context(clip->anim, clip->proxy.build_tc_flag,
1068                                                                    clip->proxy.build_size_flag, clip->proxy.quality);
1069         }
1070
1071         WM_jobs_customdata_set(wm_job, pj, proxy_freejob);
1072         WM_jobs_timer(wm_job, 0.2, NC_MOVIECLIP | ND_DISPLAY, 0);
1073         WM_jobs_callbacks(wm_job, proxy_startjob, NULL, NULL, proxy_endjob);
1074
1075         G.is_break = FALSE;
1076         WM_jobs_start(CTX_wm_manager(C), wm_job);
1077
1078         ED_area_tag_redraw(CTX_wm_area(C));
1079
1080         return OPERATOR_FINISHED;
1081 }
1082
1083 void CLIP_OT_rebuild_proxy(wmOperatorType *ot)
1084 {
1085         /* identifiers */
1086         ot->name = "Rebuild Proxy and Timecode Indices";
1087         ot->idname = "CLIP_OT_rebuild_proxy";
1088         ot->description = "Rebuild all selected proxies and timecode indices in the background";
1089
1090         /* api callbacks */
1091         ot->exec = clip_rebuild_proxy_exec;
1092         ot->poll = ED_space_clip_poll;
1093
1094         /* flags */
1095         ot->flag = OPTYPE_REGISTER;
1096 }
1097
1098 /********************** mode set operator *********************/
1099
1100 static int mode_set_exec(bContext *C, wmOperator *op)
1101 {
1102         SpaceClip *sc = CTX_wm_space_clip(C);
1103         int mode = RNA_enum_get(op->ptr, "mode");
1104
1105         sc->mode = mode;
1106
1107         WM_event_add_notifier(C, NC_SPACE | ND_SPACE_CLIP, NULL);
1108
1109         return OPERATOR_FINISHED;
1110 }
1111
1112 void CLIP_OT_mode_set(wmOperatorType *ot)
1113 {
1114         /* identifiers */
1115         ot->name = "Set Clip Mode";
1116         ot->description = "Set the clip interaction mode";
1117         ot->idname = "CLIP_OT_mode_set";
1118
1119         /* api callbacks */
1120         ot->exec = mode_set_exec;
1121
1122         ot->poll = ED_space_clip_poll;
1123
1124         /* properties */
1125         RNA_def_enum(ot->srna, "mode", clip_editor_mode_items, SC_MODE_TRACKING, "Mode", "");
1126 }
1127
1128 /********************** NDOF operator *********************/
1129
1130 /* Combined pan/zoom from a 3D mouse device.
1131  * Z zooms, XY pans
1132  * "view" (not "paper") control -- user moves the viewpoint, not the image being viewed
1133  * that explains the negative signs in the code below
1134  */
1135
1136 static int clip_view_ndof_invoke(bContext *C, wmOperator *UNUSED(op), wmEvent *event)
1137 {
1138         if (event->type != NDOF_MOTION)
1139                 return OPERATOR_CANCELLED;
1140         else {
1141                 SpaceClip *sc = CTX_wm_space_clip(C);
1142                 ARegion *ar = CTX_wm_region(C);
1143
1144                 wmNDOFMotionData *ndof = (wmNDOFMotionData *) event->customdata;
1145
1146                 float dt = ndof->dt;
1147
1148                 /* tune these until it feels right */
1149                 const float zoom_sensitivity = 0.5f;  /* 50% per second (I think) */
1150                 const float pan_sensitivity = 300.0f; /* screen pixels per second */
1151
1152                 float pan_x = pan_sensitivity * dt * ndof->tvec[0] / sc->zoom;
1153                 float pan_y = pan_sensitivity * dt * ndof->tvec[1] / sc->zoom;
1154
1155                 /* "mouse zoom" factor = 1 + (dx + dy) / 300
1156                  * what about "ndof zoom" factor? should behave like this:
1157                  * at rest -> factor = 1
1158                  * move forward -> factor > 1
1159                  * move backward -> factor < 1
1160                  */
1161                 float zoom_factor = 1.0f + zoom_sensitivity * dt * - ndof->tvec[2];
1162
1163                 if (U.ndof_flag & NDOF_ZOOM_INVERT)
1164                         zoom_factor = -zoom_factor;
1165
1166                 sclip_zoom_set_factor(C, zoom_factor, NULL);
1167                 sc->xof += pan_x;
1168                 sc->yof += pan_y;
1169
1170                 ED_region_tag_redraw(ar);
1171
1172                 return OPERATOR_FINISHED;
1173         }
1174 }
1175
1176 void CLIP_OT_view_ndof(wmOperatorType *ot)
1177 {
1178         /* identifiers */
1179         ot->name = "NDOF Pan/Zoom";
1180         ot->idname = "CLIP_OT_view_ndof";
1181         ot->description = "Use a 3D mouse device to pan/zoom the view";
1182
1183         /* api callbacks */
1184         ot->invoke = clip_view_ndof_invoke;
1185 }
1186
1187 /********************** macroses *********************/
1188
1189 void ED_operatormacros_clip(void)
1190 {
1191         wmOperatorType *ot;
1192         wmOperatorTypeMacro *otmacro;
1193
1194         ot = WM_operatortype_append_macro("CLIP_OT_add_marker_move", "Add Marker and Move",
1195                                           "Add new marker and move it on movie", OPTYPE_UNDO | OPTYPE_REGISTER);
1196         WM_operatortype_macro_define(ot, "CLIP_OT_add_marker");
1197         otmacro = WM_operatortype_macro_define(ot, "TRANSFORM_OT_translate");
1198         RNA_struct_idprops_unset(otmacro->ptr, "release_confirm");
1199
1200         ot = WM_operatortype_append_macro("CLIP_OT_add_marker_slide", "Add Marker and Slide",
1201                                           "Add new marker and slide it with mouse until mouse button release",
1202                                           OPTYPE_UNDO | OPTYPE_REGISTER);
1203         WM_operatortype_macro_define(ot, "CLIP_OT_add_marker");
1204         otmacro = WM_operatortype_macro_define(ot, "TRANSFORM_OT_translate");
1205         RNA_boolean_set(otmacro->ptr, "release_confirm", TRUE);
1206 }