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