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