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