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