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