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