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