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