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