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