Cleanup: style, use braces for editors
[blender.git] / source / blender / editors / animation / anim_markers.c
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  *
16  * The Original Code is Copyright (C) 2008 Blender Foundation.
17  * All rights reserved.
18  */
19
20 /** \file
21  * \ingroup edanimation
22  */
23
24 #include <math.h>
25
26 #include "MEM_guardedalloc.h"
27
28 #include "DNA_scene_types.h"
29 #include "DNA_object_types.h"
30
31 #include "BLI_blenlib.h"
32 #include "BLI_math.h"
33 #include "BLI_utildefines.h"
34
35 #include "BLT_translation.h"
36
37 #include "BKE_context.h"
38 #include "BKE_fcurve.h"
39 #include "BKE_layer.h"
40 #include "BKE_main.h"
41 #include "BKE_report.h"
42 #include "BKE_scene.h"
43 #include "BKE_screen.h"
44 #include "BKE_unit.h"
45
46 #include "RNA_access.h"
47 #include "RNA_define.h"
48 #include "RNA_enum_types.h"
49
50 #include "WM_api.h"
51 #include "WM_types.h"
52
53 #include "GPU_immediate.h"
54 #include "GPU_matrix.h"
55 #include "GPU_state.h"
56
57 #include "UI_interface.h"
58 #include "UI_interface_icons.h"
59 #include "UI_view2d.h"
60 #include "UI_resources.h"
61
62 #include "ED_anim_api.h"
63 #include "ED_markers.h"
64 #include "ED_screen.h"
65 #include "ED_select_utils.h"
66 #include "ED_util.h"
67 #include "ED_numinput.h"
68 #include "ED_object.h"
69 #include "ED_transform.h"
70 #include "ED_types.h"
71
72 #include "DEG_depsgraph.h"
73
74 /* ************* Marker API **************** */
75
76 /* helper function for getting the list of markers to work on */
77 static ListBase *context_get_markers(Scene *scene, ScrArea *sa)
78 {
79   /* local marker sets... */
80   if (sa) {
81     if (sa->spacetype == SPACE_ACTION) {
82       SpaceAction *saction = (SpaceAction *)sa->spacedata.first;
83
84       /* local markers can only be shown when there's only a single active action to grab them from
85        * - flag only takes effect when there's an action, otherwise it can get too confusing?
86        */
87       if (ELEM(saction->mode, SACTCONT_ACTION, SACTCONT_SHAPEKEY) && (saction->action)) {
88         if (saction->flag & SACTION_POSEMARKERS_SHOW) {
89           return &saction->action->markers;
90         }
91       }
92     }
93   }
94
95   /* default to using the scene's markers */
96   return &scene->markers;
97 }
98
99 /* ............. */
100
101 /* public API for getting markers from context */
102 ListBase *ED_context_get_markers(const bContext *C)
103 {
104   return context_get_markers(CTX_data_scene(C), CTX_wm_area(C));
105 }
106
107 /* public API for getting markers from "animation" context */
108 ListBase *ED_animcontext_get_markers(const bAnimContext *ac)
109 {
110   if (ac) {
111     return context_get_markers(ac->scene, ac->sa);
112   }
113   else {
114     return NULL;
115   }
116 }
117
118 /* --------------------------------- */
119
120 /**
121  * Apply some transformation to markers after the fact
122  *
123  * \param markers: List of markers to affect - this may or may not be the scene markers list,
124  * so don't assume anything.
125  * \param scene: Current scene (for getting current frame)
126  * \param mode: (TfmMode) transform mode that this transform is for
127  * \param value: From the transform code, this is ``t->vec[0]``
128  * (which is delta transform for grab/extend, and scale factor for scale)
129  * \param side: (B/L/R) for 'extend' functionality, which side of current frame to use
130  */
131 int ED_markers_post_apply_transform(
132     ListBase *markers, Scene *scene, int mode, float value, char side)
133 {
134   TimeMarker *marker;
135   float cfra = (float)CFRA;
136   int changed_tot = 0;
137
138   /* sanity check - no markers, or locked markers */
139   if ((scene->toolsettings->lock_markers) || (markers == NULL)) {
140     return changed_tot;
141   }
142
143   /* affect selected markers - it's unlikely that we will want to affect all in this way? */
144   for (marker = markers->first; marker; marker = marker->next) {
145     if (marker->flag & SELECT) {
146       switch (mode) {
147         case TFM_TIME_TRANSLATE:
148         case TFM_TIME_EXTEND: {
149           /* apply delta if marker is on the right side of the current frame */
150           if ((side == 'B') || (side == 'L' && marker->frame < cfra) ||
151               (side == 'R' && marker->frame >= cfra)) {
152             marker->frame += round_fl_to_int(value);
153             changed_tot++;
154           }
155           break;
156         }
157         case TFM_TIME_SCALE: {
158           /* rescale the distance between the marker and the current frame */
159           marker->frame = cfra + round_fl_to_int((float)(marker->frame - cfra) * value);
160           changed_tot++;
161           break;
162         }
163       }
164     }
165   }
166
167   return changed_tot;
168 }
169
170 /* --------------------------------- */
171
172 /* Get the marker that is closest to this point */
173 /* XXX for select, the min_dist should be small */
174 TimeMarker *ED_markers_find_nearest_marker(ListBase *markers, float x)
175 {
176   TimeMarker *marker, *nearest = NULL;
177   float dist, min_dist = 1000000;
178
179   if (markers) {
180     for (marker = markers->first; marker; marker = marker->next) {
181       dist = fabsf((float)marker->frame - x);
182
183       if (dist < min_dist) {
184         min_dist = dist;
185         nearest = marker;
186       }
187     }
188   }
189
190   return nearest;
191 }
192
193 /* Return the time of the marker that occurs on a frame closest to the given time */
194 int ED_markers_find_nearest_marker_time(ListBase *markers, float x)
195 {
196   TimeMarker *nearest = ED_markers_find_nearest_marker(markers, x);
197   return (nearest) ? (nearest->frame) : round_fl_to_int(x);
198 }
199
200 void ED_markers_get_minmax(ListBase *markers, short sel, float *first, float *last)
201 {
202   TimeMarker *marker;
203   float min, max;
204
205   /* sanity check */
206   //printf("markers = %p -  %p, %p\n", markers, markers->first, markers->last);
207   if (ELEM(NULL, markers, markers->first, markers->last)) {
208     *first = 0.0f;
209     *last = 0.0f;
210     return;
211   }
212
213   min = FLT_MAX;
214   max = -FLT_MAX;
215   for (marker = markers->first; marker; marker = marker->next) {
216     if (!sel || (marker->flag & SELECT)) {
217       if (marker->frame < min) {
218         min = (float)marker->frame;
219       }
220       if (marker->frame > max) {
221         max = (float)marker->frame;
222       }
223     }
224   }
225
226   /* set the min/max values */
227   *first = min;
228   *last = max;
229 }
230
231 /* --------------------------------- */
232
233 /* Adds a marker to list of cfra elems */
234 static void add_marker_to_cfra_elem(ListBase *lb, TimeMarker *marker, short only_sel)
235 {
236   CfraElem *ce, *cen;
237
238   /* should this one only be considered if it is selected? */
239   if ((only_sel) && ((marker->flag & SELECT) == 0)) {
240     return;
241   }
242
243   /* insertion sort - try to find a previous cfra elem */
244   for (ce = lb->first; ce; ce = ce->next) {
245     if (ce->cfra == marker->frame) {
246       /* do because of double keys */
247       if (marker->flag & SELECT) {
248         ce->sel = marker->flag;
249       }
250       return;
251     }
252     else if (ce->cfra > marker->frame) {
253       break;
254     }
255   }
256
257   cen = MEM_callocN(sizeof(CfraElem), "add_to_cfra_elem");
258   if (ce) {
259     BLI_insertlinkbefore(lb, ce, cen);
260   }
261   else {
262     BLI_addtail(lb, cen);
263   }
264
265   cen->cfra = marker->frame;
266   cen->sel = marker->flag;
267 }
268
269 /* This function makes a list of all the markers. The only_sel
270  * argument is used to specify whether only the selected markers
271  * are added.
272  */
273 void ED_markers_make_cfra_list(ListBase *markers, ListBase *lb, short only_sel)
274 {
275   TimeMarker *marker;
276
277   if (lb) {
278     /* Clear the list first, since callers have no way of knowing
279      * whether this terminated early otherwise. This may lead
280      * to crashes if the user didn't clear the memory first.
281      */
282     lb->first = lb->last = NULL;
283   }
284   else {
285     return;
286   }
287
288   if (markers == NULL) {
289     return;
290   }
291
292   for (marker = markers->first; marker; marker = marker->next) {
293     add_marker_to_cfra_elem(lb, marker, only_sel);
294   }
295 }
296
297 void ED_markers_deselect_all(ListBase *markers, int action)
298 {
299   if (action == SEL_TOGGLE) {
300     action = ED_markers_get_first_selected(markers) ? SEL_DESELECT : SEL_SELECT;
301   }
302
303   for (TimeMarker *marker = markers->first; marker; marker = marker->next) {
304     if (action == SEL_SELECT) {
305       marker->flag |= SELECT;
306     }
307     else if (action == SEL_DESELECT) {
308       marker->flag &= ~SELECT;
309     }
310     else if (action == SEL_INVERT) {
311       marker->flag ^= SELECT;
312     }
313     else {
314       BLI_assert(0);
315     }
316   }
317 }
318
319 /* --------------------------------- */
320
321 /* Get the first selected marker */
322 TimeMarker *ED_markers_get_first_selected(ListBase *markers)
323 {
324   TimeMarker *marker;
325
326   if (markers) {
327     for (marker = markers->first; marker; marker = marker->next) {
328       if (marker->flag & SELECT) {
329         return marker;
330       }
331     }
332   }
333
334   return NULL;
335 }
336
337 /* --------------------------------- */
338
339 /* Print debugging prints of list of markers
340  * BSI's: do NOT make static or put in if-defs as "unused code".
341  * That's too much trouble when we need to use for quick debugging!
342  */
343 void debug_markers_print_list(ListBase *markers)
344 {
345   TimeMarker *marker;
346
347   if (markers == NULL) {
348     printf("No markers list to print debug for\n");
349     return;
350   }
351
352   printf("List of markers follows: -----\n");
353
354   for (marker = markers->first; marker; marker = marker->next) {
355     printf(
356         "\t'%s' on %d at %p with %u\n", marker->name, marker->frame, (void *)marker, marker->flag);
357   }
358
359   printf("End of list ------------------\n");
360 }
361
362 /* ************* Marker Drawing ************ */
363
364 static void draw_marker_name(const uiFontStyle *fstyle,
365                              TimeMarker *marker,
366                              const char *name,
367                              int cfra,
368                              const float xpos,
369                              const float ypixels)
370 {
371   unsigned char text_col[4];
372   float x, y;
373
374   /* minimal y coordinate which wouldn't be occluded by scroll */
375   int min_y = 17.0f * UI_DPI_FAC;
376
377   if (marker->flag & SELECT) {
378     UI_GetThemeColor4ubv(TH_TEXT_HI, text_col);
379     x = xpos + 4.0f * UI_DPI_FAC;
380     y = (ypixels <= 39.0f * UI_DPI_FAC) ? (ypixels - 10.0f * UI_DPI_FAC) : 29.0f * UI_DPI_FAC;
381     y = max_ii(y, min_y);
382   }
383   else {
384     UI_GetThemeColor4ubv(TH_TEXT, text_col);
385     if ((marker->frame <= cfra) && (marker->frame + 5 > cfra)) {
386       x = xpos + 8.0f * UI_DPI_FAC;
387       y = (ypixels <= 39.0f * UI_DPI_FAC) ? (ypixels - 10.0f * UI_DPI_FAC) : 29.0f * UI_DPI_FAC;
388       y = max_ii(y, min_y);
389     }
390     else {
391       x = xpos + 8.0f * UI_DPI_FAC;
392       y = 17.0f * UI_DPI_FAC;
393     }
394   }
395
396 #ifdef DURIAN_CAMERA_SWITCH
397   if (marker->camera && (marker->camera->restrictflag & OB_RESTRICT_RENDER)) {
398     text_col[3] = 100;
399   }
400 #endif
401
402   UI_fontstyle_draw_simple(fstyle, x, y, name, text_col);
403 }
404
405 static void draw_marker_line(const float color[4], float x, float ymin, float ymax)
406 {
407   GPUVertFormat *format = immVertexFormat();
408   uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
409
410   immBindBuiltinProgram(GPU_SHADER_2D_LINE_DASHED_UNIFORM_COLOR);
411
412   float viewport_size[4];
413   GPU_viewport_size_get_f(viewport_size);
414   immUniform2f("viewport_size", viewport_size[2] / UI_DPI_FAC, viewport_size[3] / UI_DPI_FAC);
415
416   immUniformColor4fv(color);
417   immUniform1i("colors_len", 0); /* "simple" mode */
418   immUniform1f("dash_width", 6.0f);
419   immUniform1f("dash_factor", 0.5f);
420
421   immBegin(GPU_PRIM_LINES, 2);
422   immVertex2f(pos, x, ymin);
423   immVertex2f(pos, x, ymax);
424   immEnd();
425
426   immUnbindProgram();
427 }
428
429 /* function to draw markers */
430 static void draw_marker(const uiFontStyle *fstyle,
431                         TimeMarker *marker,
432                         int cfra,
433                         int flag,
434                         /* avoid re-calculating each time */
435                         const float ypixels,
436                         const float xscale,
437                         int height)
438 {
439   const float xpos = marker->frame * xscale;
440 #ifdef DURIAN_CAMERA_SWITCH
441   const float yoffs = (marker->camera) ? 0.2f * UI_DPI_ICON_SIZE : 0.0f;
442 #else
443   const float yoffs = 0.0f;
444 #endif
445   int icon_id;
446
447   GPU_blend(true);
448   GPU_blend_set_func_separate(
449       GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA);
450
451   /* vertical line - dotted */
452 #ifdef DURIAN_CAMERA_SWITCH
453   if ((marker->camera) || (flag & DRAW_MARKERS_LINES))
454 #else
455   if (flag & DRAW_MARKERS_LINES)
456 #endif
457   {
458     float color[4];
459     if (marker->flag & SELECT) {
460       copy_v4_fl4(color, 1.0f, 1.0f, 1.0f, 0.38f);
461     }
462     else {
463       copy_v4_fl4(color, 0.0f, 0.0f, 0.0f, 0.38f);
464     }
465
466     draw_marker_line(color, xpos, yoffs + 1.5f * UI_DPI_ICON_SIZE, height);
467   }
468
469   /* 5 px to offset icon to align properly, space / pixels corrects for zoom */
470   if (flag & DRAW_MARKERS_LOCAL) {
471     icon_id = (marker->flag & ACTIVE) ? ICON_PMARKER_ACT :
472                                         (marker->flag & SELECT) ? ICON_PMARKER_SEL : ICON_PMARKER;
473   }
474 #ifdef DURIAN_CAMERA_SWITCH
475   else if (marker->camera) {
476     icon_id = (marker->flag & SELECT) ? ICON_OUTLINER_OB_CAMERA : ICON_CAMERA_DATA;
477   }
478 #endif
479   else {
480     icon_id = (marker->flag & SELECT) ? ICON_MARKER_HLT : ICON_MARKER;
481   }
482
483   UI_icon_draw(xpos - 0.55f * UI_DPI_ICON_SIZE, yoffs + UI_DPI_ICON_SIZE, icon_id);
484
485   GPU_blend(false);
486
487   /* and the marker name too, shifted slightly to the top-right */
488 #ifdef DURIAN_CAMERA_SWITCH
489   if (marker->camera) {
490     draw_marker_name(fstyle, marker, marker->camera->id.name + 2, cfra, xpos, ypixels);
491   }
492   else if (marker->name[0]) {
493     draw_marker_name(fstyle, marker, marker->name, cfra, xpos, ypixels);
494   }
495 #else
496   if (marker->name[0]) {
497     draw_marker_name(fstyle, marker, marker->name, cfra, xpos, ypixels);
498   }
499 #endif
500 }
501
502 /* Draw Scene-Markers in time window */
503 void ED_markers_draw(const bContext *C, int flag)
504 {
505   const uiFontStyle *fstyle = UI_FSTYLE_WIDGET;
506   ListBase *markers = ED_context_get_markers(C);
507   View2D *v2d;
508   TimeMarker *marker;
509   Scene *scene;
510   int select_pass;
511   int v2d_clip_range_x[2];
512   float font_width_max;
513
514   /* cache values */
515   float ypixels, xscale, yscale;
516
517   if (markers == NULL || BLI_listbase_is_empty(markers)) {
518     return;
519   }
520
521   scene = CTX_data_scene(C);
522   v2d = UI_view2d_fromcontext(C);
523   int height = v2d->mask.ymax - v2d->mask.ymin;
524
525   if (flag & DRAW_MARKERS_MARGIN) {
526     uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
527     immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
528
529     const unsigned char shade[4] = {0, 0, 0, 16};
530     immUniformColor4ubv(shade);
531
532     GPU_blend(true);
533     GPU_blend_set_func_separate(
534         GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA);
535
536     immRectf(pos, v2d->cur.xmin, 0, v2d->cur.xmax, UI_MARKER_MARGIN_Y);
537
538     GPU_blend(false);
539
540     immUnbindProgram();
541   }
542
543   /* no time correction for framelen! space is drawn with old values */
544   ypixels = BLI_rcti_size_y(&v2d->mask);
545   UI_view2d_scale_get(v2d, &xscale, &yscale);
546   GPU_matrix_push();
547   GPU_matrix_scale_2f(1.0f / xscale, 1.0f);
548
549   /* x-bounds with offset for text (adjust for long string, avoid checking string width) */
550   font_width_max = (10 * UI_DPI_FAC) / xscale;
551   v2d_clip_range_x[0] = v2d->cur.xmin - (sizeof(marker->name) * font_width_max);
552   v2d_clip_range_x[1] = v2d->cur.xmax + font_width_max;
553
554   /* loop [unselected, selected] */
555   for (select_pass = 0; select_pass <= SELECT; select_pass += SELECT) {
556     /* unselected markers are drawn at the first time */
557     for (marker = markers->first; marker; marker = marker->next) {
558       if ((marker->flag & SELECT) == select_pass) {
559         /* bounds check */
560         if ((marker->frame >= v2d_clip_range_x[0]) && (marker->frame <= v2d_clip_range_x[1])) {
561           draw_marker(fstyle, marker, scene->r.cfra, flag, ypixels, xscale, height);
562         }
563       }
564     }
565   }
566
567   GPU_matrix_pop();
568 }
569
570 /* ************************ Marker Wrappers API ********************* */
571 /* These wrappers allow marker operators to function within the confines
572  * of standard animation editors, such that they can coexist with the
573  * primary operations of those editors.
574  */
575
576 /* ------------------------ */
577
578 /* special poll() which checks if there are selected markers first */
579 static bool ed_markers_poll_selected_markers(bContext *C)
580 {
581   ListBase *markers = ED_context_get_markers(C);
582
583   /* first things first: markers can only exist in timeline views */
584   if (ED_operator_animview_active(C) == 0) {
585     return 0;
586   }
587
588   /* check if some marker is selected */
589   return ED_markers_get_first_selected(markers) != NULL;
590 }
591
592 static bool ed_markers_poll_selected_no_locked_markers(bContext *C)
593 {
594   ListBase *markers = ED_context_get_markers(C);
595   ToolSettings *ts = CTX_data_tool_settings(C);
596
597   if (ts->lock_markers) {
598     return 0;
599   }
600
601   /* first things first: markers can only exist in timeline views */
602   if (ED_operator_animview_active(C) == 0) {
603     return 0;
604   }
605
606   /* check if some marker is selected */
607   return ED_markers_get_first_selected(markers) != NULL;
608 }
609
610 /* special poll() which checks if there are any markers at all first */
611 static bool ed_markers_poll_markers_exist(bContext *C)
612 {
613   ListBase *markers = ED_context_get_markers(C);
614   ToolSettings *ts = CTX_data_tool_settings(C);
615
616   if (ts->lock_markers) {
617     return 0;
618   }
619
620   /* first things first: markers can only exist in timeline views */
621   if (ED_operator_animview_active(C) == 0) {
622     return 0;
623   }
624
625   /* list of markers must exist, as well as some markers in it! */
626   return (markers && markers->first);
627 }
628
629 /* ------------------------ */
630
631 /**
632  * Second-tier invoke() callback that performs context validation before running the
633  * "custom"/third-tier invoke() callback supplied as the last arg (which would normally
634  * be the operator's invoke() callback elsewhere)
635  *
636  * \param invoke_func: "standard" invoke function that operator would otherwise have used.
637  * If NULL, the operator's standard exec()
638  * callback will be called instead in the appropriate places.
639  */
640 static int ed_markers_opwrap_invoke_custom(bContext *C,
641                                            wmOperator *op,
642                                            const wmEvent *event,
643                                            int (*invoke_func)(bContext *,
644                                                               wmOperator *,
645                                                               const wmEvent *))
646 {
647   int retval = OPERATOR_PASS_THROUGH;
648
649   /* removed check for Y coord of event, keymap has bounbox now */
650
651   /* allow operator to run now */
652   if (invoke_func) {
653     retval = invoke_func(C, op, event);
654   }
655   else if (op->type->exec) {
656     retval = op->type->exec(C, op);
657   }
658   else {
659     BKE_report(op->reports,
660                RPT_ERROR,
661                "Programming error: operator does not actually have code to do anything!");
662   }
663
664   /* unless successful, must add "pass-through"
665    * to let normal operator's have a chance at tackling this event */
666   if ((retval & (OPERATOR_FINISHED | OPERATOR_INTERFACE)) == 0) {
667     retval |= OPERATOR_PASS_THROUGH;
668   }
669
670   return retval;
671 }
672
673 /* standard wrapper - first-tier invoke() callback to be directly assigned to operator typedata
674  * for operators which don't need any special invoke calls. Any operators with special invoke calls
675  * though will need to implement their own wrapper which calls the second-tier callback themselves
676  * (passing through the custom invoke function they use)
677  */
678 static int ed_markers_opwrap_invoke(bContext *C, wmOperator *op, const wmEvent *event)
679 {
680   return ed_markers_opwrap_invoke_custom(C, op, event, NULL);
681 }
682
683 /* ************************** add markers *************************** */
684
685 /* add TimeMarker at current frame */
686 static int ed_marker_add_exec(bContext *C, wmOperator *UNUSED(op))
687 {
688   ListBase *markers = ED_context_get_markers(C);
689   TimeMarker *marker;
690   int frame = CTX_data_scene(C)->r.cfra;
691
692   if (markers == NULL) {
693     return OPERATOR_CANCELLED;
694   }
695
696   /* prefer not having 2 markers at the same place,
697    * though the user can move them to overlap once added */
698   for (marker = markers->first; marker; marker = marker->next) {
699     if (marker->frame == frame) {
700       return OPERATOR_CANCELLED;
701     }
702   }
703
704   /* deselect all */
705   for (marker = markers->first; marker; marker = marker->next) {
706     marker->flag &= ~SELECT;
707   }
708
709   marker = MEM_callocN(sizeof(TimeMarker), "TimeMarker");
710   marker->flag = SELECT;
711   marker->frame = frame;
712   BLI_snprintf(marker->name, sizeof(marker->name), "F_%02d", frame);  // XXX - temp code only
713   BLI_addtail(markers, marker);
714
715   WM_event_add_notifier(C, NC_SCENE | ND_MARKERS, NULL);
716   WM_event_add_notifier(C, NC_ANIMATION | ND_MARKERS, NULL);
717
718   return OPERATOR_FINISHED;
719 }
720
721 static void MARKER_OT_add(wmOperatorType *ot)
722 {
723   /* identifiers */
724   ot->name = "Add Time Marker";
725   ot->description = "Add a new time marker";
726   ot->idname = "MARKER_OT_add";
727
728   /* api callbacks */
729   ot->exec = ed_marker_add_exec;
730   ot->invoke = ed_markers_opwrap_invoke;
731   ot->poll = ED_operator_animview_active;
732
733   /* flags */
734   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
735 }
736
737 /* ************************** transform markers *************************** */
738
739 /* operator state vars used:
740  *     frs: delta movement
741  *
742  * functions:
743  *
744  *     init()   check selection, add customdata with old values and some lookups
745  *
746  *     apply()  do the actual movement
747  *
748  *     exit()    cleanup, send notifier
749  *
750  *     cancel() to escape from modal
751  *
752  * callbacks:
753  *
754  *     exec()    calls init, apply, exit
755  *
756  *     invoke() calls init, adds modal handler
757  *
758  *     modal()    accept modal events while doing it, ends with apply and exit, or cancel
759  */
760
761 typedef struct MarkerMove {
762   SpaceLink *slink;
763   ListBase *markers;
764   int event_type; /* store invoke-event, to verify */
765   int *oldframe, evtx, firstx;
766   NumInput num;
767 } MarkerMove;
768
769 static bool ed_marker_move_use_time(MarkerMove *mm)
770 {
771   if (((mm->slink->spacetype == SPACE_SEQ) && !(((SpaceSeq *)mm->slink)->flag & SEQ_DRAWFRAMES)) ||
772       ((mm->slink->spacetype == SPACE_ACTION) &&
773        (((SpaceAction *)mm->slink)->flag & SACTION_DRAWTIME)) ||
774       ((mm->slink->spacetype == SPACE_GRAPH) &&
775        !(((SpaceGraph *)mm->slink)->flag & SIPO_DRAWTIME)) ||
776       ((mm->slink->spacetype == SPACE_NLA) && !(((SpaceNla *)mm->slink)->flag & SNLA_DRAWTIME))) {
777     return true;
778   }
779
780   return false;
781 }
782
783 static void ed_marker_move_update_header(bContext *C, wmOperator *op)
784 {
785   Scene *scene = CTX_data_scene(C);
786   MarkerMove *mm = op->customdata;
787   TimeMarker *marker, *selmarker = NULL;
788   const int offs = RNA_int_get(op->ptr, "frames");
789   char str[UI_MAX_DRAW_STR];
790   char str_offs[NUM_STR_REP_LEN];
791   int totmark;
792   const bool use_time = ed_marker_move_use_time(mm);
793
794   for (totmark = 0, marker = mm->markers->first; marker; marker = marker->next) {
795     if (marker->flag & SELECT) {
796       selmarker = marker;
797       totmark++;
798     }
799   }
800
801   if (hasNumInput(&mm->num)) {
802     outputNumInput(&mm->num, str_offs, &scene->unit);
803   }
804   else if (use_time) {
805     BLI_snprintf(str_offs, sizeof(str_offs), "%.2f", FRA2TIME(offs));
806   }
807   else {
808     BLI_snprintf(str_offs, sizeof(str_offs), "%d", offs);
809   }
810
811   if (totmark == 1 && selmarker) {
812     /* we print current marker value */
813     if (use_time) {
814       BLI_snprintf(
815           str, sizeof(str), IFACE_("Marker %.2f offset %s"), FRA2TIME(selmarker->frame), str_offs);
816     }
817     else {
818       BLI_snprintf(str, sizeof(str), IFACE_("Marker %d offset %s"), selmarker->frame, str_offs);
819     }
820   }
821   else {
822     BLI_snprintf(str, sizeof(str), IFACE_("Marker offset %s"), str_offs);
823   }
824
825   ED_area_status_text(CTX_wm_area(C), str);
826 }
827
828 /* copy selection to temp buffer */
829 /* return 0 if not OK */
830 static bool ed_marker_move_init(bContext *C, wmOperator *op)
831 {
832   Scene *scene = CTX_data_scene(C);
833   ListBase *markers = ED_context_get_markers(C);
834   MarkerMove *mm;
835   TimeMarker *marker;
836   int a, totmark;
837
838   if (markers == NULL) {
839     return false;
840   }
841
842   for (totmark = 0, marker = markers->first; marker; marker = marker->next) {
843     if (marker->flag & SELECT) {
844       totmark++;
845     }
846   }
847
848   if (totmark == 0) {
849     return false;
850   }
851
852   op->customdata = mm = MEM_callocN(sizeof(MarkerMove), "Markermove");
853   mm->slink = CTX_wm_space_data(C);
854   mm->markers = markers;
855   mm->oldframe = MEM_callocN(totmark * sizeof(int), "MarkerMove oldframe");
856
857   initNumInput(&mm->num);
858   mm->num.idx_max = 0; /* one axis */
859   mm->num.val_flag[0] |= NUM_NO_FRACTION;
860   mm->num.unit_sys = scene->unit.system;
861   /* No time unit supporting frames currently... */
862   mm->num.unit_type[0] = ed_marker_move_use_time(mm) ? B_UNIT_TIME : B_UNIT_NONE;
863
864   for (a = 0, marker = markers->first; marker; marker = marker->next) {
865     if (marker->flag & SELECT) {
866       mm->oldframe[a] = marker->frame;
867       a++;
868     }
869   }
870
871   return true;
872 }
873
874 /* free stuff */
875 static void ed_marker_move_exit(bContext *C, wmOperator *op)
876 {
877   MarkerMove *mm = op->customdata;
878
879   /* free data */
880   MEM_freeN(mm->oldframe);
881   MEM_freeN(op->customdata);
882   op->customdata = NULL;
883
884   /* clear custom header prints */
885   ED_area_status_text(CTX_wm_area(C), NULL);
886 }
887
888 static int ed_marker_move_invoke(bContext *C, wmOperator *op, const wmEvent *event)
889 {
890   if (ed_marker_move_init(C, op)) {
891     MarkerMove *mm = op->customdata;
892
893     mm->evtx = event->x;
894     mm->firstx = event->x;
895     mm->event_type = event->type;
896
897     /* add temp handler */
898     WM_event_add_modal_handler(C, op);
899
900     /* reset frs delta */
901     RNA_int_set(op->ptr, "frames", 0);
902
903     ed_marker_move_update_header(C, op);
904
905     return OPERATOR_RUNNING_MODAL;
906   }
907
908   return OPERATOR_CANCELLED;
909 }
910
911 static int ed_marker_move_invoke_wrapper(bContext *C, wmOperator *op, const wmEvent *event)
912 {
913   return ed_markers_opwrap_invoke_custom(C, op, event, ed_marker_move_invoke);
914 }
915
916 /* note, init has to be called successfully */
917 static void ed_marker_move_apply(bContext *C, wmOperator *op)
918 {
919 #ifdef DURIAN_CAMERA_SWITCH
920   bScreen *sc = CTX_wm_screen(C);
921   Scene *scene = CTX_data_scene(C);
922   Object *camera = scene->camera;
923 #endif
924   MarkerMove *mm = op->customdata;
925   TimeMarker *marker;
926   int a, offs;
927
928   offs = RNA_int_get(op->ptr, "frames");
929   for (a = 0, marker = mm->markers->first; marker; marker = marker->next) {
930     if (marker->flag & SELECT) {
931       marker->frame = mm->oldframe[a] + offs;
932       a++;
933     }
934   }
935
936   WM_event_add_notifier(C, NC_SCENE | ND_MARKERS, NULL);
937   WM_event_add_notifier(C, NC_ANIMATION | ND_MARKERS, NULL);
938
939 #ifdef DURIAN_CAMERA_SWITCH
940   /* so we get view3d redraws */
941   BKE_scene_camera_switch_update(scene);
942
943   if (camera != scene->camera) {
944     BKE_screen_view3d_scene_sync(sc, scene);
945     WM_event_add_notifier(C, NC_SCENE | NA_EDITED, scene);
946   }
947 #endif
948 }
949
950 /* only for modal */
951 static void ed_marker_move_cancel(bContext *C, wmOperator *op)
952 {
953   RNA_int_set(op->ptr, "frames", 0);
954   ed_marker_move_apply(C, op);
955   ed_marker_move_exit(C, op);
956 }
957
958 static int ed_marker_move_modal(bContext *C, wmOperator *op, const wmEvent *event)
959 {
960   Scene *scene = CTX_data_scene(C);
961   MarkerMove *mm = op->customdata;
962   View2D *v2d = UI_view2d_fromcontext(C);
963   const bool has_numinput = hasNumInput(&mm->num);
964   const bool use_time = ed_marker_move_use_time(mm);
965
966   /* Modal numinput active, try to handle numeric inputs first... */
967   if (event->val == KM_PRESS && has_numinput && handleNumInput(C, &mm->num, event)) {
968     float value = (float)RNA_int_get(op->ptr, "frames");
969
970     applyNumInput(&mm->num, &value);
971     if (use_time) {
972       value = TIME2FRA(value);
973     }
974
975     RNA_int_set(op->ptr, "frames", (int)value);
976     ed_marker_move_apply(C, op);
977     ed_marker_move_update_header(C, op);
978   }
979   else {
980     bool handled = false;
981     switch (event->type) {
982       case ESCKEY:
983         ed_marker_move_cancel(C, op);
984         return OPERATOR_CANCELLED;
985       case RIGHTMOUSE:
986         /* press = user manually demands transform to be canceled */
987         if (event->val == KM_PRESS) {
988           ed_marker_move_cancel(C, op);
989           return OPERATOR_CANCELLED;
990         }
991         /* else continue; <--- see if release event should be caught for tweak-end */
992         ATTR_FALLTHROUGH;
993
994       case RETKEY:
995       case PADENTER:
996       case LEFTMOUSE:
997       case MIDDLEMOUSE:
998         if (WM_event_is_modal_tweak_exit(event, mm->event_type)) {
999           ed_marker_move_exit(C, op);
1000           WM_event_add_notifier(C, NC_SCENE | ND_MARKERS, NULL);
1001           WM_event_add_notifier(C, NC_ANIMATION | ND_MARKERS, NULL);
1002           return OPERATOR_FINISHED;
1003         }
1004         break;
1005       case MOUSEMOVE:
1006         if (!has_numinput) {
1007           float dx;
1008
1009           dx = BLI_rctf_size_x(&v2d->cur) / BLI_rcti_size_x(&v2d->mask);
1010
1011           if (event->x != mm->evtx) { /* XXX maybe init for first time */
1012             float fac;
1013
1014             mm->evtx = event->x;
1015             fac = ((float)(event->x - mm->firstx) * dx);
1016
1017             apply_keyb_grid(event->shift,
1018                             event->ctrl,
1019                             &fac,
1020                             0.0,
1021                             1.0,
1022                             0.1,
1023                             0 /*was: U.flag & USER_AUTOGRABGRID*/);
1024
1025             RNA_int_set(op->ptr, "frames", (int)fac);
1026             ed_marker_move_apply(C, op);
1027             ed_marker_move_update_header(C, op);
1028           }
1029         }
1030         break;
1031     }
1032
1033     if (!handled && event->val == KM_PRESS && handleNumInput(C, &mm->num, event)) {
1034       float value = (float)RNA_int_get(op->ptr, "frames");
1035
1036       applyNumInput(&mm->num, &value);
1037       if (use_time) {
1038         value = TIME2FRA(value);
1039       }
1040
1041       RNA_int_set(op->ptr, "frames", (int)value);
1042       ed_marker_move_apply(C, op);
1043       ed_marker_move_update_header(C, op);
1044     }
1045   }
1046
1047   return OPERATOR_RUNNING_MODAL;
1048 }
1049
1050 static int ed_marker_move_exec(bContext *C, wmOperator *op)
1051 {
1052   if (ed_marker_move_init(C, op)) {
1053     ed_marker_move_apply(C, op);
1054     ed_marker_move_exit(C, op);
1055     return OPERATOR_FINISHED;
1056   }
1057   return OPERATOR_PASS_THROUGH;
1058 }
1059
1060 static void MARKER_OT_move(wmOperatorType *ot)
1061 {
1062   /* identifiers */
1063   ot->name = "Move Time Marker";
1064   ot->description = "Move selected time marker(s)";
1065   ot->idname = "MARKER_OT_move";
1066
1067   /* api callbacks */
1068   ot->exec = ed_marker_move_exec;
1069   ot->invoke = ed_marker_move_invoke_wrapper;
1070   ot->modal = ed_marker_move_modal;
1071   ot->poll = ed_markers_poll_selected_no_locked_markers;
1072   ot->cancel = ed_marker_move_cancel;
1073
1074   /* flags */
1075   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING | OPTYPE_GRAB_CURSOR;
1076
1077   /* rna storage */
1078   RNA_def_int(ot->srna, "frames", 0, INT_MIN, INT_MAX, "Frames", "", INT_MIN, INT_MAX);
1079 }
1080
1081 /* ************************** duplicate markers *************************** */
1082
1083 /* operator state vars used:
1084  *     frs: delta movement
1085  *
1086  * functions:
1087  *
1088  *     apply()  do the actual duplicate
1089  *
1090  * callbacks:
1091  *
1092  *     exec()    calls apply, move_exec
1093  *
1094  *     invoke() calls apply, move_invoke
1095  *
1096  *     modal()    uses move_modal
1097  */
1098
1099 /* duplicate selected TimeMarkers */
1100 static void ed_marker_duplicate_apply(bContext *C)
1101 {
1102   ListBase *markers = ED_context_get_markers(C);
1103   TimeMarker *marker, *newmarker;
1104
1105   if (markers == NULL) {
1106     return;
1107   }
1108
1109   /* go through the list of markers, duplicate selected markers and add duplicated copies
1110    * to the beginning of the list (unselect original markers)
1111    */
1112   for (marker = markers->first; marker; marker = marker->next) {
1113     if (marker->flag & SELECT) {
1114       /* unselect selected marker */
1115       marker->flag &= ~SELECT;
1116
1117       /* create and set up new marker */
1118       newmarker = MEM_callocN(sizeof(TimeMarker), "TimeMarker");
1119       newmarker->flag = SELECT;
1120       newmarker->frame = marker->frame;
1121       BLI_strncpy(newmarker->name, marker->name, sizeof(marker->name));
1122
1123 #ifdef DURIAN_CAMERA_SWITCH
1124       newmarker->camera = marker->camera;
1125 #endif
1126
1127       /* new marker is added to the beginning of list */
1128       // FIXME: bad ordering!
1129       BLI_addhead(markers, newmarker);
1130     }
1131   }
1132 }
1133
1134 static int ed_marker_duplicate_exec(bContext *C, wmOperator *op)
1135 {
1136   ed_marker_duplicate_apply(C);
1137   ed_marker_move_exec(C, op); /* assumes frs delta set */
1138
1139   return OPERATOR_FINISHED;
1140 }
1141
1142 static int ed_marker_duplicate_invoke(bContext *C, wmOperator *op, const wmEvent *event)
1143 {
1144   ed_marker_duplicate_apply(C);
1145   return ed_marker_move_invoke(C, op, event);
1146 }
1147
1148 static int ed_marker_duplicate_invoke_wrapper(bContext *C, wmOperator *op, const wmEvent *event)
1149 {
1150   return ed_markers_opwrap_invoke_custom(C, op, event, ed_marker_duplicate_invoke);
1151 }
1152
1153 static void MARKER_OT_duplicate(wmOperatorType *ot)
1154 {
1155   /* identifiers */
1156   ot->name = "Duplicate Time Marker";
1157   ot->description = "Duplicate selected time marker(s)";
1158   ot->idname = "MARKER_OT_duplicate";
1159
1160   /* api callbacks */
1161   ot->exec = ed_marker_duplicate_exec;
1162   ot->invoke = ed_marker_duplicate_invoke_wrapper;
1163   ot->modal = ed_marker_move_modal;
1164   ot->poll = ed_markers_poll_selected_no_locked_markers;
1165   ot->cancel = ed_marker_move_cancel;
1166
1167   /* flags */
1168   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1169
1170   /* rna storage */
1171   RNA_def_int(ot->srna, "frames", 0, INT_MIN, INT_MAX, "Frames", "", INT_MIN, INT_MAX);
1172 }
1173
1174 /* ************************** selection ************************************/
1175
1176 /* select/deselect TimeMarker at current frame */
1177 static void select_timeline_marker_frame(ListBase *markers, int frame, bool extend)
1178 {
1179   TimeMarker *marker, *marker_first = NULL;
1180
1181   /* support for selection cycling */
1182   for (marker = markers->first; marker; marker = marker->next) {
1183     if (marker->frame == frame) {
1184       if (marker->flag & SELECT) {
1185         marker_first = marker->next;
1186         break;
1187       }
1188     }
1189   }
1190
1191   /* if extend is not set, then deselect markers */
1192   if (extend == false) {
1193     for (marker = markers->first; marker; marker = marker->next) {
1194       marker->flag &= ~SELECT;
1195     }
1196   }
1197
1198   LISTBASE_CIRCULAR_FORWARD_BEGIN (markers, marker, marker_first) {
1199     /* this way a not-extend select will always give 1 selected marker */
1200     if (marker->frame == frame) {
1201       marker->flag ^= SELECT;
1202       break;
1203     }
1204   }
1205   LISTBASE_CIRCULAR_FORWARD_END(markers, marker, marker_first);
1206 }
1207
1208 static int ed_marker_select(bContext *C, const wmEvent *event, bool extend, bool camera)
1209 {
1210   ListBase *markers = ED_context_get_markers(C);
1211   ARegion *ar = CTX_wm_region(C);
1212   View2D *v2d = UI_view2d_fromcontext(C);
1213   float viewx;
1214   int x, cfra;
1215
1216   if (markers == NULL) {
1217     return OPERATOR_PASS_THROUGH;
1218   }
1219
1220   x = event->x - ar->winrct.xmin;
1221
1222   viewx = UI_view2d_region_to_view_x(v2d, x);
1223
1224   cfra = ED_markers_find_nearest_marker_time(markers, viewx);
1225
1226   select_timeline_marker_frame(markers, cfra, extend);
1227
1228 #ifdef DURIAN_CAMERA_SWITCH
1229
1230   if (camera) {
1231     Scene *scene = CTX_data_scene(C);
1232     ViewLayer *view_layer = CTX_data_view_layer(C);
1233     Base *base;
1234     TimeMarker *marker;
1235     int sel = 0;
1236
1237     if (!extend) {
1238       BKE_view_layer_base_deselect_all(view_layer);
1239     }
1240
1241     for (marker = markers->first; marker; marker = marker->next) {
1242       if (marker->frame == cfra) {
1243         sel = (marker->flag & SELECT);
1244         break;
1245       }
1246     }
1247
1248     for (marker = markers->first; marker; marker = marker->next) {
1249       if (marker->camera) {
1250         if (marker->frame == cfra) {
1251           base = BKE_view_layer_base_find(view_layer, marker->camera);
1252           if (base) {
1253             ED_object_base_select(base, sel);
1254             if (sel) {
1255               ED_object_base_activate(C, base);
1256             }
1257           }
1258         }
1259       }
1260     }
1261
1262     DEG_id_tag_update(&scene->id, ID_RECALC_SELECT);
1263     WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene);
1264   }
1265 #else
1266   (void)camera;
1267 #endif
1268
1269   WM_event_add_notifier(C, NC_SCENE | ND_MARKERS, NULL);
1270   WM_event_add_notifier(C, NC_ANIMATION | ND_MARKERS, NULL);
1271
1272   /* allowing tweaks, but needs OPERATOR_FINISHED, otherwise renaming fails... [#25987] */
1273   return OPERATOR_FINISHED | OPERATOR_PASS_THROUGH;
1274 }
1275
1276 static int ed_marker_select_invoke(bContext *C, wmOperator *op, const wmEvent *event)
1277 {
1278   const bool extend = RNA_boolean_get(op->ptr, "extend");
1279   bool camera = false;
1280 #ifdef DURIAN_CAMERA_SWITCH
1281   camera = RNA_boolean_get(op->ptr, "camera");
1282 #endif
1283   return ed_marker_select(C, event, extend, camera);
1284 }
1285
1286 static int ed_marker_select_invoke_wrapper(bContext *C, wmOperator *op, const wmEvent *event)
1287 {
1288   return ed_markers_opwrap_invoke_custom(C, op, event, ed_marker_select_invoke);
1289 }
1290
1291 static void MARKER_OT_select(wmOperatorType *ot)
1292 {
1293   PropertyRNA *prop;
1294
1295   /* identifiers */
1296   ot->name = "Select Time Marker";
1297   ot->description = "Select time marker(s)";
1298   ot->idname = "MARKER_OT_select";
1299
1300   /* api callbacks */
1301   ot->invoke = ed_marker_select_invoke_wrapper;
1302   ot->poll = ed_markers_poll_markers_exist;
1303
1304   /* flags */
1305   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1306
1307   prop = RNA_def_boolean(ot->srna, "extend", 0, "Extend", "Extend the selection");
1308   RNA_def_property_flag(prop, PROP_SKIP_SAVE);
1309 #ifdef DURIAN_CAMERA_SWITCH
1310   prop = RNA_def_boolean(ot->srna, "camera", 0, "Camera", "Select the camera");
1311   RNA_def_property_flag(prop, PROP_SKIP_SAVE);
1312 #endif
1313 }
1314
1315 /* *************************** box select markers **************** */
1316
1317 /* operator state vars used: (added by default WM callbacks)
1318  * xmin, ymin
1319  * xmax, ymax
1320  *
1321  * customdata: the wmGesture pointer, with subwindow
1322  *
1323  * callbacks:
1324  *
1325  *  exec()  has to be filled in by user
1326  *
1327  *  invoke() default WM function
1328  *          adds modal handler
1329  *
1330  *  modal() default WM function
1331  *          accept modal events while doing it, calls exec(), handles ESC and border drawing
1332  *
1333  *  poll()  has to be filled in by user for context
1334  */
1335
1336 static int ed_marker_box_select_exec(bContext *C, wmOperator *op)
1337 {
1338   View2D *v2d = UI_view2d_fromcontext(C);
1339   ListBase *markers = ED_context_get_markers(C);
1340   rctf rect;
1341
1342   WM_operator_properties_border_to_rctf(op, &rect);
1343   UI_view2d_region_to_view_rctf(v2d, &rect, &rect);
1344
1345   if (markers == NULL) {
1346     return 0;
1347   }
1348
1349   const eSelectOp sel_op = RNA_enum_get(op->ptr, "mode");
1350   const bool select = (sel_op != SEL_OP_SUB);
1351   if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
1352     ED_markers_deselect_all(markers, SEL_DESELECT);
1353   }
1354
1355   for (TimeMarker *marker = markers->first; marker; marker = marker->next) {
1356     if (BLI_rctf_isect_x(&rect, marker->frame)) {
1357       SET_FLAG_FROM_TEST(marker->flag, select, SELECT);
1358     }
1359   }
1360
1361   WM_event_add_notifier(C, NC_SCENE | ND_MARKERS, NULL);
1362   WM_event_add_notifier(C, NC_ANIMATION | ND_MARKERS, NULL);
1363
1364   return 1;
1365 }
1366
1367 static int ed_marker_select_box_invoke_wrapper(bContext *C, wmOperator *op, const wmEvent *event)
1368 {
1369   return ed_markers_opwrap_invoke_custom(C, op, event, WM_gesture_box_invoke);
1370 }
1371
1372 static void MARKER_OT_select_box(wmOperatorType *ot)
1373 {
1374   /* identifiers */
1375   ot->name = "Marker Box Select";
1376   ot->description = "Select all time markers using box selection";
1377   ot->idname = "MARKER_OT_select_box";
1378
1379   /* api callbacks */
1380   ot->exec = ed_marker_box_select_exec;
1381   ot->invoke = ed_marker_select_box_invoke_wrapper;
1382   ot->modal = WM_gesture_box_modal;
1383   ot->cancel = WM_gesture_box_cancel;
1384
1385   ot->poll = ed_markers_poll_markers_exist;
1386
1387   /* flags */
1388   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1389
1390   /* properties */
1391   WM_operator_properties_gesture_box(ot);
1392   WM_operator_properties_select_operation_simple(ot);
1393 }
1394
1395 /* *********************** (de)select all ***************** */
1396
1397 static int ed_marker_select_all_exec(bContext *C, wmOperator *op)
1398 {
1399   ListBase *markers = ED_context_get_markers(C);
1400   if (markers == NULL) {
1401     return OPERATOR_CANCELLED;
1402   }
1403
1404   int action = RNA_enum_get(op->ptr, "action");
1405   ED_markers_deselect_all(markers, action);
1406
1407   WM_event_add_notifier(C, NC_SCENE | ND_MARKERS, NULL);
1408   WM_event_add_notifier(C, NC_ANIMATION | ND_MARKERS, NULL);
1409
1410   return OPERATOR_FINISHED;
1411 }
1412
1413 static void MARKER_OT_select_all(wmOperatorType *ot)
1414 {
1415   /* identifiers */
1416   ot->name = "(De)select all Markers";
1417   ot->description = "Change selection of all time markers";
1418   ot->idname = "MARKER_OT_select_all";
1419
1420   /* api callbacks */
1421   ot->exec = ed_marker_select_all_exec;
1422   ot->invoke = ed_markers_opwrap_invoke;
1423   ot->poll = ed_markers_poll_markers_exist;
1424
1425   /* flags */
1426   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1427
1428   /* rna */
1429   WM_operator_properties_select_all(ot);
1430 }
1431
1432 /* ***************** remove marker *********************** */
1433
1434 /* remove selected TimeMarkers */
1435 static int ed_marker_delete_exec(bContext *C, wmOperator *UNUSED(op))
1436 {
1437   ListBase *markers = ED_context_get_markers(C);
1438   TimeMarker *marker, *nmarker;
1439   bool changed = false;
1440
1441   if (markers == NULL) {
1442     return OPERATOR_CANCELLED;
1443   }
1444
1445   for (marker = markers->first; marker; marker = nmarker) {
1446     nmarker = marker->next;
1447     if (marker->flag & SELECT) {
1448       BLI_freelinkN(markers, marker);
1449       changed = true;
1450     }
1451   }
1452
1453   if (changed) {
1454     WM_event_add_notifier(C, NC_SCENE | ND_MARKERS, NULL);
1455     WM_event_add_notifier(C, NC_ANIMATION | ND_MARKERS, NULL);
1456   }
1457
1458   return OPERATOR_FINISHED;
1459 }
1460
1461 static int ed_marker_delete_invoke_wrapper(bContext *C, wmOperator *op, const wmEvent *event)
1462 {
1463   // XXX: must we keep these confirmations?
1464   return ed_markers_opwrap_invoke_custom(C, op, event, WM_operator_confirm);
1465 }
1466
1467 static void MARKER_OT_delete(wmOperatorType *ot)
1468 {
1469   /* identifiers */
1470   ot->name = "Delete Markers";
1471   ot->description = "Delete selected time marker(s)";
1472   ot->idname = "MARKER_OT_delete";
1473
1474   /* api callbacks */
1475   ot->invoke = ed_marker_delete_invoke_wrapper;
1476   ot->exec = ed_marker_delete_exec;
1477   ot->poll = ed_markers_poll_selected_no_locked_markers;
1478
1479   /* flags */
1480   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1481 }
1482
1483 /* **************** rename marker ***************** */
1484
1485 /* rename first selected TimeMarker */
1486 static int ed_marker_rename_exec(bContext *C, wmOperator *op)
1487 {
1488   TimeMarker *marker = ED_markers_get_first_selected(ED_context_get_markers(C));
1489
1490   if (marker) {
1491     RNA_string_get(op->ptr, "name", marker->name);
1492
1493     WM_event_add_notifier(C, NC_SCENE | ND_MARKERS, NULL);
1494     WM_event_add_notifier(C, NC_ANIMATION | ND_MARKERS, NULL);
1495
1496     return OPERATOR_FINISHED;
1497   }
1498   else {
1499     return OPERATOR_CANCELLED;
1500   }
1501 }
1502
1503 static int ed_marker_rename_invoke_wrapper(bContext *C, wmOperator *op, const wmEvent *event)
1504 {
1505   /* must initialize the marker name first if there is a marker selected */
1506   TimeMarker *marker = ED_markers_get_first_selected(ED_context_get_markers(C));
1507   if (marker) {
1508     RNA_string_set(op->ptr, "name", marker->name);
1509   }
1510
1511   /* now see if the operator is usable */
1512   return ed_markers_opwrap_invoke_custom(C, op, event, WM_operator_props_popup_confirm);
1513 }
1514
1515 static void MARKER_OT_rename(wmOperatorType *ot)
1516 {
1517   /* identifiers */
1518   ot->name = "Rename Marker";
1519   ot->description = "Rename first selected time marker";
1520   ot->idname = "MARKER_OT_rename";
1521
1522   /* api callbacks */
1523   ot->invoke = ed_marker_rename_invoke_wrapper;
1524   ot->exec = ed_marker_rename_exec;
1525   ot->poll = ed_markers_poll_selected_no_locked_markers;
1526
1527   /* flags */
1528   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1529
1530   /* properties */
1531   ot->prop = RNA_def_string(ot->srna,
1532                             "name",
1533                             "RenamedMarker",
1534                             sizeof(((TimeMarker *)NULL)->name),
1535                             "Name",
1536                             "New name for marker");
1537 #if 0
1538   RNA_def_boolean(ot->srna,
1539                   "ensure_unique",
1540                   0,
1541                   "Ensure Unique",
1542                   "Ensure that new name is unique within collection of markers");
1543 #endif
1544 }
1545
1546 /* **************** make links to scene ***************** */
1547
1548 static int ed_marker_make_links_scene_exec(bContext *C, wmOperator *op)
1549 {
1550   ListBase *markers = ED_context_get_markers(C);
1551   Scene *scene_to = BLI_findlink(&CTX_data_main(C)->scenes, RNA_enum_get(op->ptr, "scene"));
1552   TimeMarker *marker, *marker_new;
1553
1554   if (scene_to == NULL) {
1555     BKE_report(op->reports, RPT_ERROR, "Scene not found");
1556     return OPERATOR_CANCELLED;
1557   }
1558
1559   if (scene_to == CTX_data_scene(C)) {
1560     BKE_report(op->reports, RPT_ERROR, "Cannot re-link markers into the same scene");
1561     return OPERATOR_CANCELLED;
1562   }
1563
1564   if (scene_to->toolsettings->lock_markers) {
1565     BKE_report(op->reports, RPT_ERROR, "Target scene has locked markers");
1566     return OPERATOR_CANCELLED;
1567   }
1568
1569   /* copy markers */
1570   for (marker = markers->first; marker; marker = marker->next) {
1571     if (marker->flag & SELECT) {
1572       marker_new = MEM_dupallocN(marker);
1573       marker_new->prev = marker_new->next = NULL;
1574
1575       BLI_addtail(&scene_to->markers, marker_new);
1576     }
1577   }
1578
1579   return OPERATOR_FINISHED;
1580 }
1581
1582 static int ed_marker_make_links_scene_invoke_wrapper(bContext *C,
1583                                                      wmOperator *op,
1584                                                      const wmEvent *event)
1585 {
1586   return ed_markers_opwrap_invoke_custom(C, op, event, WM_menu_invoke);
1587 }
1588
1589 static void MARKER_OT_make_links_scene(wmOperatorType *ot)
1590 {
1591   PropertyRNA *prop;
1592
1593   /* identifiers */
1594   ot->name = "Make Links to Scene";
1595   ot->description = "Copy selected markers to another scene";
1596   ot->idname = "MARKER_OT_make_links_scene";
1597
1598   /* api callbacks */
1599   ot->exec = ed_marker_make_links_scene_exec;
1600   ot->invoke = ed_marker_make_links_scene_invoke_wrapper;
1601   ot->poll = ed_markers_poll_selected_markers;
1602
1603   /* flags */
1604   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1605
1606   /* properties */
1607   prop = RNA_def_enum(ot->srna, "scene", DummyRNA_NULL_items, 0, "Scene", "");
1608   RNA_def_enum_funcs(prop, RNA_scene_itemf);
1609   RNA_def_property_flag(prop, PROP_ENUM_NO_TRANSLATE);
1610   ot->prop = prop;
1611 }
1612
1613 #ifdef DURIAN_CAMERA_SWITCH
1614 /* ******************************* camera bind marker ***************** */
1615
1616 static int ed_marker_camera_bind_exec(bContext *C, wmOperator *op)
1617 {
1618   bScreen *sc = CTX_wm_screen(C);
1619   Scene *scene = CTX_data_scene(C);
1620   Object *ob = CTX_data_active_object(C);
1621   ListBase *markers = ED_context_get_markers(C);
1622   TimeMarker *marker;
1623
1624   /* Don't do anything if we don't have a camera selected */
1625   if (ob == NULL) {
1626     BKE_report(op->reports, RPT_ERROR, "Select a camera to bind to a marker on this frame");
1627     return OPERATOR_CANCELLED;
1628   }
1629
1630   /* add new marker, unless we already have one on this frame, in which case, replace it */
1631   if (markers == NULL) {
1632     return OPERATOR_CANCELLED;
1633   }
1634
1635   marker = ED_markers_find_nearest_marker(markers, CFRA);
1636   if ((marker == NULL) || (marker->frame != CFRA)) {
1637     marker = MEM_callocN(sizeof(TimeMarker), "Camera TimeMarker");
1638     marker->flag = SELECT;
1639     marker->frame = CFRA;
1640     BLI_addtail(markers, marker);
1641
1642     /* deselect all others, so that the user can then move it without problems */
1643     for (TimeMarker *m = markers->first; m; m = m->next) {
1644       if (m != marker) {
1645         m->flag &= ~SELECT;
1646       }
1647     }
1648   }
1649
1650   /* bind to the nominated camera (as set in operator props) */
1651   marker->camera = ob;
1652
1653   /* camera may have changes */
1654   BKE_scene_camera_switch_update(scene);
1655   BKE_screen_view3d_scene_sync(sc, scene);
1656
1657   WM_event_add_notifier(C, NC_SCENE | ND_MARKERS, NULL);
1658   WM_event_add_notifier(C, NC_ANIMATION | ND_MARKERS, NULL);
1659   WM_event_add_notifier(C, NC_SCENE | NA_EDITED, scene); /* so we get view3d redraws */
1660
1661   return OPERATOR_FINISHED;
1662 }
1663
1664 static void MARKER_OT_camera_bind(wmOperatorType *ot)
1665 {
1666   /* identifiers */
1667   ot->name = "Bind Camera to Markers";
1668   ot->description = "Bind the selected camera to a marker on the current frame";
1669   ot->idname = "MARKER_OT_camera_bind";
1670
1671   /* api callbacks */
1672   ot->exec = ed_marker_camera_bind_exec;
1673   ot->invoke = ed_markers_opwrap_invoke;
1674   ot->poll = ED_operator_animview_active;
1675
1676   /* flags */
1677   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1678 }
1679 #endif
1680
1681 /* ************************** registration **********************************/
1682
1683 /* called in screen_ops.c:ED_operatortypes_screen() */
1684 void ED_operatortypes_marker(void)
1685 {
1686   WM_operatortype_append(MARKER_OT_add);
1687   WM_operatortype_append(MARKER_OT_move);
1688   WM_operatortype_append(MARKER_OT_duplicate);
1689   WM_operatortype_append(MARKER_OT_select);
1690   WM_operatortype_append(MARKER_OT_select_box);
1691   WM_operatortype_append(MARKER_OT_select_all);
1692   WM_operatortype_append(MARKER_OT_delete);
1693   WM_operatortype_append(MARKER_OT_rename);
1694   WM_operatortype_append(MARKER_OT_make_links_scene);
1695 #ifdef DURIAN_CAMERA_SWITCH
1696   WM_operatortype_append(MARKER_OT_camera_bind);
1697 #endif
1698 }
1699
1700 /* called in screen_ops.c:ED_keymap_screen() */
1701 void ED_keymap_marker(wmKeyConfig *keyconf)
1702 {
1703   WM_keymap_ensure(keyconf, "Markers", 0, 0);
1704 }