WM: refactor gestures for use as tools
[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 += round_fl_to_int(value);
153                                                 changed_tot++;
154                                         }
155                                         break;
156                                 }
157                                 case TFM_TIME_SCALE:
158                                 {
159                                         /* rescale the distance between the marker and the current frame */
160                                         marker->frame = cfra + round_fl_to_int((float)(marker->frame - cfra) * value);
161                                         changed_tot++;
162                                         break;
163                                 }
164                         }
165                 }
166         }
167         
168         return changed_tot;
169 }
170
171 /* --------------------------------- */
172
173 /* Get the marker that is closest to this point */
174 /* XXX for select, the min_dist should be small */
175 TimeMarker *ED_markers_find_nearest_marker(ListBase *markers, float x)
176 {
177         TimeMarker *marker, *nearest = NULL;
178         float dist, min_dist = 1000000;
179         
180         if (markers) {
181                 for (marker = markers->first; marker; marker = marker->next) {
182                         dist = fabsf((float)marker->frame - x);
183                         
184                         if (dist < min_dist) {
185                                 min_dist = dist;
186                                 nearest = marker;
187                         }
188                 }
189         }
190         
191         return nearest;
192 }
193
194 /* Return the time of the marker that occurs on a frame closest to the given time */
195 int ED_markers_find_nearest_marker_time(ListBase *markers, float x)
196 {
197         TimeMarker *nearest = ED_markers_find_nearest_marker(markers, x);
198         return (nearest) ? (nearest->frame) : round_fl_to_int(x);
199 }
200
201
202 void ED_markers_get_minmax(ListBase *markers, short sel, float *first, float *last)
203 {
204         TimeMarker *marker;
205         float min, max;
206         
207         /* sanity check */
208         //printf("markers = %p -  %p, %p\n", markers, markers->first, markers->last);
209         if (ELEM(NULL, markers, markers->first, markers->last)) {
210                 *first = 0.0f;
211                 *last = 0.0f;
212                 return;
213         }
214
215         min = FLT_MAX;
216         max = -FLT_MAX;
217         for (marker = markers->first; marker; marker = marker->next) {
218                 if (!sel || (marker->flag & SELECT)) {
219                         if (marker->frame < min)
220                                 min = (float)marker->frame;
221                         if (marker->frame > max)
222                                 max = (float)marker->frame;
223                 }
224         }
225         
226         /* set the min/max values */
227         *first = min;
228         *last = max;
229 }
230
231 /* --------------------------------- */
232
233 /* Adds a marker to list of cfra elems */
234 static void add_marker_to_cfra_elem(ListBase *lb, TimeMarker *marker, short only_sel)
235 {
236         CfraElem *ce, *cen;
237         
238         /* should this one only be considered if it is selected? */
239         if ((only_sel) && ((marker->flag & SELECT) == 0))
240                 return;
241         
242         /* insertion sort - try to find a previous cfra elem */
243         for (ce = lb->first; ce; ce = ce->next) {
244                 if (ce->cfra == marker->frame) {
245                         /* do because of double keys */
246                         if (marker->flag & SELECT) 
247                                 ce->sel = marker->flag;
248                         return;
249                 }
250                 else if (ce->cfra > marker->frame) {
251                         break;
252                 }
253         }
254         
255         cen = MEM_callocN(sizeof(CfraElem), "add_to_cfra_elem");
256         if (ce) BLI_insertlinkbefore(lb, ce, cen);
257         else BLI_addtail(lb, cen);
258
259         cen->cfra = marker->frame;
260         cen->sel = marker->flag;
261 }
262
263 /* This function makes a list of all the markers. The only_sel
264  * argument is used to specify whether only the selected markers
265  * are added.
266  */
267 void ED_markers_make_cfra_list(ListBase *markers, ListBase *lb, short only_sel)
268 {
269         TimeMarker *marker;
270         
271         if (lb) {
272                 /* Clear the list first, since callers have no way of knowing
273                  * whether this terminated early otherwise. This may lead
274                  * to crashes if the user didn't clear the memory first.
275                  */
276                 lb->first = lb->last = NULL;
277         }
278         else {
279                 return;
280         }
281         
282         if (markers == NULL) {
283                 return;
284         }
285         
286         for (marker = markers->first; marker; marker = marker->next)
287                 add_marker_to_cfra_elem(lb, marker, only_sel);
288 }
289
290 /* --------------------------------- */
291
292 /* Get the first selected marker */
293 TimeMarker *ED_markers_get_first_selected(ListBase *markers)
294 {
295         TimeMarker *marker;
296         
297         if (markers) {
298                 for (marker = markers->first; marker; marker = marker->next) {
299                         if (marker->flag & SELECT)
300                                 return marker;
301                 }
302         }
303         
304         return NULL;
305 }
306
307 /* --------------------------------- */
308
309 /* Print debugging prints of list of markers 
310  * BSI's: do NOT make static or put in if-defs as "unused code". That's too much trouble when we need to use for quick debugging!
311  */
312 void debug_markers_print_list(ListBase *markers)
313 {
314         TimeMarker *marker;
315         
316         if (markers == NULL) {
317                 printf("No markers list to print debug for\n");
318                 return;
319         }
320         
321         printf("List of markers follows: -----\n");
322         
323         for (marker = markers->first; marker; marker = marker->next) {
324                 printf("\t'%s' on %d at %p with %u\n", marker->name, marker->frame, (void *)marker, marker->flag);
325         }
326         
327         printf("End of list ------------------\n");
328 }
329
330 /* ************* Marker Drawing ************ */
331
332 /* 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                                 ATTR_FALLTHROUGH;
893
894                         case RETKEY:
895                         case PADENTER:
896                         case LEFTMOUSE:
897                         case MIDDLEMOUSE:
898                                 if (WM_event_is_modal_tweak_exit(event, mm->event_type)) {
899                                         ed_marker_move_exit(C, op);
900                                         WM_event_add_notifier(C, NC_SCENE | ND_MARKERS, NULL);
901                                         WM_event_add_notifier(C, NC_ANIMATION | ND_MARKERS, NULL);
902                                         return OPERATOR_FINISHED;
903                                 }
904                                 break;
905                         case MOUSEMOVE:
906                                 if (!has_numinput) {
907                                         float dx;
908
909                                         dx = BLI_rctf_size_x(&v2d->cur) / BLI_rcti_size_x(&v2d->mask);
910
911                                         if (event->x != mm->evtx) {   /* XXX maybe init for first time */
912                                                 float fac;
913
914                                                 mm->evtx = event->x;
915                                                 fac = ((float)(event->x - mm->firstx) * dx);
916
917                                                 if (mm->slink->spacetype == SPACE_TIME)
918                                                         apply_keyb_grid(event->shift, event->ctrl, &fac, 0.0, FPS, 0.1 * FPS, 0);
919                                                 else
920                                                         apply_keyb_grid(event->shift, event->ctrl, &fac, 0.0, 1.0, 0.1, 0 /*was: U.flag & USER_AUTOGRABGRID*/);
921
922                                                 RNA_int_set(op->ptr, "frames", (int)fac);
923                                                 ed_marker_move_apply(C, op);
924                                                 ed_marker_move_update_header(C, op);
925                                         }
926                                 }
927                                 break;
928                 }
929
930                 if (!handled && event->val == KM_PRESS && handleNumInput(C, &mm->num, event)) {
931                         float value = (float)RNA_int_get(op->ptr, "frames");
932
933                         applyNumInput(&mm->num, &value);
934                         if (use_time) {
935                                 value = TIME2FRA(value);
936                         }
937
938                         RNA_int_set(op->ptr, "frames", (int)value);
939                         ed_marker_move_apply(C, op);
940                         ed_marker_move_update_header(C, op);
941                 }
942         }
943
944         return OPERATOR_RUNNING_MODAL;
945 }
946
947 static int ed_marker_move_exec(bContext *C, wmOperator *op)
948 {
949         if (ed_marker_move_init(C, op)) {
950                 ed_marker_move_apply(C, op);
951                 ed_marker_move_exit(C, op);
952                 return OPERATOR_FINISHED;
953         }
954         return OPERATOR_PASS_THROUGH;
955 }
956
957 static void MARKER_OT_move(wmOperatorType *ot)
958 {
959         /* identifiers */
960         ot->name = "Move Time Marker";
961         ot->description = "Move selected time marker(s)";
962         ot->idname = "MARKER_OT_move";
963         
964         /* api callbacks */
965         ot->exec = ed_marker_move_exec;
966         ot->invoke = ed_marker_move_invoke_wrapper;
967         ot->modal = ed_marker_move_modal;
968         ot->poll = ed_markers_poll_selected_no_locked_markers;
969         ot->cancel = ed_marker_move_cancel;
970         
971         /* flags */
972         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING | OPTYPE_GRAB_CURSOR;
973         
974         /* rna storage */
975         RNA_def_int(ot->srna, "frames", 0, INT_MIN, INT_MAX, "Frames", "", INT_MIN, INT_MAX);
976 }
977
978 /* ************************** duplicate markers *************************** */
979
980 /* operator state vars used:
981  *     frs: delta movement
982  *
983  * functions:
984  *
985  *     apply()  do the actual duplicate
986  *
987  * callbacks:
988  *
989  *     exec()    calls apply, move_exec
990  *
991  *     invoke() calls apply, move_invoke
992  *
993  *     modal()    uses move_modal
994  */
995
996 /* duplicate selected TimeMarkers */
997 static void ed_marker_duplicate_apply(bContext *C)
998 {
999         ListBase *markers = ED_context_get_markers(C);
1000         TimeMarker *marker, *newmarker;
1001         
1002         if (markers == NULL) 
1003                 return;
1004
1005         /* go through the list of markers, duplicate selected markers and add duplicated copies
1006          * to the beginning of the list (unselect original markers)
1007          */
1008         for (marker = markers->first; marker; marker = marker->next) {
1009                 if (marker->flag & SELECT) {
1010                         /* unselect selected marker */
1011                         marker->flag &= ~SELECT;
1012                         
1013                         /* create and set up new marker */
1014                         newmarker = MEM_callocN(sizeof(TimeMarker), "TimeMarker");
1015                         newmarker->flag = SELECT;
1016                         newmarker->frame = marker->frame;
1017                         BLI_strncpy(newmarker->name, marker->name, sizeof(marker->name));
1018                         
1019 #ifdef DURIAN_CAMERA_SWITCH
1020                         newmarker->camera = marker->camera;
1021 #endif
1022
1023                         /* new marker is added to the beginning of list */
1024                         // FIXME: bad ordering!
1025                         BLI_addhead(markers, newmarker);
1026                 }
1027         }
1028 }
1029
1030 static int ed_marker_duplicate_exec(bContext *C, wmOperator *op)
1031 {
1032         ed_marker_duplicate_apply(C);
1033         ed_marker_move_exec(C, op); /* assumes frs delta set */
1034         
1035         return OPERATOR_FINISHED;
1036         
1037 }
1038
1039 static int ed_marker_duplicate_invoke(bContext *C, wmOperator *op, const wmEvent *event)
1040 {
1041         ed_marker_duplicate_apply(C);
1042         return ed_marker_move_invoke(C, op, event);
1043 }
1044
1045 static int ed_marker_duplicate_invoke_wrapper(bContext *C, wmOperator *op, const wmEvent *event)
1046 {
1047         return ed_markers_opwrap_invoke_custom(C, op, event, ed_marker_duplicate_invoke);
1048 }
1049
1050 static void MARKER_OT_duplicate(wmOperatorType *ot)
1051 {
1052         /* identifiers */
1053         ot->name = "Duplicate Time Marker";
1054         ot->description = "Duplicate selected time marker(s)";
1055         ot->idname = "MARKER_OT_duplicate";
1056         
1057         /* api callbacks */
1058         ot->exec = ed_marker_duplicate_exec;
1059         ot->invoke = ed_marker_duplicate_invoke_wrapper;
1060         ot->modal = ed_marker_move_modal;
1061         ot->poll = ed_markers_poll_selected_no_locked_markers;
1062         ot->cancel = ed_marker_move_cancel;
1063         
1064         /* flags */
1065         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1066         
1067         /* rna storage */
1068         RNA_def_int(ot->srna, "frames", 0, INT_MIN, INT_MAX, "Frames", "", INT_MIN, INT_MAX);
1069 }
1070
1071 /* ************************** selection ************************************/
1072
1073 /* select/deselect TimeMarker at current frame */
1074 static void select_timeline_marker_frame(ListBase *markers, int frame, bool extend)
1075 {
1076         TimeMarker *marker, *marker_first = NULL;
1077
1078         /* support for selection cycling */
1079         for (marker = markers->first; marker; marker = marker->next) {
1080                 if (marker->frame == frame) {
1081                         if (marker->flag & SELECT) {
1082                                 marker_first = marker->next;
1083                                 break;
1084                         }
1085                 }
1086         }
1087
1088         /* if extend is not set, then deselect markers */
1089         if (extend == false) {
1090                 for (marker = markers->first; marker; marker = marker->next) {
1091                         marker->flag &= ~SELECT;
1092                 }
1093         }
1094
1095         BLI_LISTBASE_CIRCULAR_FORWARD_BEGIN (markers, marker, marker_first) {
1096                 /* this way a not-extend select will always give 1 selected marker */
1097                 if (marker->frame == frame) {
1098                         marker->flag ^= SELECT;
1099                         break;
1100                 }
1101         }
1102         BLI_LISTBASE_CIRCULAR_FORWARD_END (markers, marker, marker_first);
1103 }
1104
1105 static int ed_marker_select(bContext *C, const wmEvent *event, bool extend, bool camera)
1106 {
1107         ListBase *markers = ED_context_get_markers(C);
1108         ARegion *ar = CTX_wm_region(C);
1109         View2D *v2d = UI_view2d_fromcontext(C);
1110         float viewx;
1111         int x, cfra;
1112         
1113         if (markers == NULL)
1114                 return OPERATOR_PASS_THROUGH;
1115
1116         x = event->x - ar->winrct.xmin;
1117         
1118         viewx = UI_view2d_region_to_view_x(v2d, x);
1119         
1120         cfra = ED_markers_find_nearest_marker_time(markers, viewx);
1121         
1122         select_timeline_marker_frame(markers, cfra, extend);
1123         
1124 #ifdef DURIAN_CAMERA_SWITCH
1125
1126         if (camera) {
1127                 Scene *scene = CTX_data_scene(C);
1128                 Base *base;
1129                 TimeMarker *marker;
1130                 int sel = 0;
1131                 
1132                 if (!extend)
1133                         BKE_scene_base_deselect_all(scene);
1134                 
1135                 for (marker = markers->first; marker; marker = marker->next) {
1136                         if (marker->frame == cfra) {
1137                                 sel = (marker->flag & SELECT);
1138                                 break;
1139                         }
1140                 }
1141                 
1142                 for (marker = markers->first; marker; marker = marker->next) {
1143                         if (marker->camera) {
1144                                 if (marker->frame == cfra) {
1145                                         base = BKE_scene_base_find(scene, marker->camera);
1146                                         if (base) {
1147                                                 ED_base_object_select(base, sel);
1148                                                 if (sel)
1149                                                         ED_base_object_activate(C, base);
1150                                         }
1151                                 }
1152                         }
1153                 }
1154                 
1155                 WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene);
1156         }
1157 #else
1158         (void)camera;
1159 #endif
1160
1161         WM_event_add_notifier(C, NC_SCENE | ND_MARKERS, NULL);
1162         WM_event_add_notifier(C, NC_ANIMATION | ND_MARKERS, NULL);
1163
1164         /* allowing tweaks, but needs OPERATOR_FINISHED, otherwise renaming fails... [#25987] */
1165         return OPERATOR_FINISHED | OPERATOR_PASS_THROUGH;
1166 }
1167
1168 static int ed_marker_select_invoke(bContext *C, wmOperator *op, const wmEvent *event)
1169 {
1170         const bool extend = RNA_boolean_get(op->ptr, "extend");
1171         bool camera = false;
1172 #ifdef DURIAN_CAMERA_SWITCH
1173         camera = RNA_boolean_get(op->ptr, "camera");
1174 #endif
1175         return ed_marker_select(C, event, extend, camera);
1176 }
1177
1178 static int ed_marker_select_invoke_wrapper(bContext *C, wmOperator *op, const wmEvent *event)
1179 {
1180         return ed_markers_opwrap_invoke_custom(C, op, event, ed_marker_select_invoke);
1181 }
1182
1183 static void MARKER_OT_select(wmOperatorType *ot)
1184 {
1185         PropertyRNA *prop;
1186
1187         /* identifiers */
1188         ot->name = "Select Time Marker";
1189         ot->description = "Select time marker(s)";
1190         ot->idname = "MARKER_OT_select";
1191         
1192         /* api callbacks */
1193         ot->invoke = ed_marker_select_invoke_wrapper;
1194         ot->poll = ed_markers_poll_markers_exist;
1195         
1196         /* flags */
1197         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1198
1199         prop = RNA_def_boolean(ot->srna, "extend", 0, "Extend", "Extend the selection");
1200         RNA_def_property_flag(prop, PROP_SKIP_SAVE);
1201 #ifdef DURIAN_CAMERA_SWITCH
1202         prop = RNA_def_boolean(ot->srna, "camera", 0, "Camera", "Select the camera");
1203         RNA_def_property_flag(prop, PROP_SKIP_SAVE);
1204 #endif
1205 }
1206
1207 /* *************************** border select markers **************** */
1208
1209 /* operator state vars used: (added by default WM callbacks)   
1210  * xmin, ymin
1211  * xmax, ymax
1212  *
1213  * customdata: the wmGesture pointer, with subwindow
1214  *
1215  * callbacks:
1216  *
1217  *  exec()      has to be filled in by user
1218  *
1219  *  invoke() default WM function
1220  *          adds modal handler
1221  *
1222  *  modal()     default WM function
1223  *          accept modal events while doing it, calls exec(), handles ESC and border drawing
1224  *
1225  *  poll()      has to be filled in by user for context
1226  */
1227
1228 static int ed_marker_border_select_exec(bContext *C, wmOperator *op)
1229 {
1230         View2D *v2d = UI_view2d_fromcontext(C);
1231         ListBase *markers = ED_context_get_markers(C);
1232         TimeMarker *marker;
1233         bool select = !RNA_boolean_get(op->ptr, "deselect");
1234         bool extend = RNA_boolean_get(op->ptr, "extend");
1235         rctf rect;
1236         
1237         WM_operator_properties_border_to_rctf(op, &rect);
1238         UI_view2d_region_to_view_rctf(v2d, &rect, &rect);
1239         
1240         if (markers == NULL)
1241                 return 0;
1242         
1243         /* XXX marker context */
1244         for (marker = markers->first; marker; marker = marker->next) {
1245                 if (BLI_rctf_isect_x(&rect, marker->frame)) {
1246                         if (select) {
1247                                 marker->flag |= SELECT;
1248                         }
1249                         else {
1250                                 marker->flag &= ~SELECT;
1251                         }
1252                 }
1253                 else if (!extend) {
1254                         marker->flag &= ~SELECT;
1255                 }
1256         }
1257         
1258         WM_event_add_notifier(C, NC_SCENE | ND_MARKERS, NULL);
1259         WM_event_add_notifier(C, NC_ANIMATION | ND_MARKERS, NULL);
1260
1261         return 1;
1262 }
1263
1264 static int ed_marker_select_border_invoke_wrapper(bContext *C, wmOperator *op, const wmEvent *event)
1265 {
1266         return ed_markers_opwrap_invoke_custom(C, op, event, WM_gesture_border_invoke);
1267 }
1268
1269 static void MARKER_OT_select_border(wmOperatorType *ot)
1270 {
1271         /* identifiers */
1272         ot->name = "Marker Border Select";
1273         ot->description = "Select all time markers using border selection";
1274         ot->idname = "MARKER_OT_select_border";
1275         
1276         /* api callbacks */
1277         ot->exec = ed_marker_border_select_exec;
1278         ot->invoke = ed_marker_select_border_invoke_wrapper;
1279         ot->modal = WM_gesture_border_modal;
1280         ot->cancel = WM_gesture_border_cancel;
1281         
1282         ot->poll = ed_markers_poll_markers_exist;
1283         
1284         /* flags */
1285         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1286         
1287         /* rna */
1288         WM_operator_properties_gesture_border_select(ot);
1289 }
1290
1291 /* *********************** (de)select all ***************** */
1292
1293 static int ed_marker_select_all_exec(bContext *C, wmOperator *op)
1294 {
1295         ListBase *markers = ED_context_get_markers(C);
1296         TimeMarker *marker;
1297         int action = RNA_enum_get(op->ptr, "action");
1298
1299         if (markers == NULL)
1300                 return OPERATOR_CANCELLED;
1301
1302         if (action == SEL_TOGGLE) {
1303                 action = (ED_markers_get_first_selected(markers) != NULL) ? SEL_DESELECT : SEL_SELECT;
1304         }
1305         
1306         for (marker = markers->first; marker; marker = marker->next) {
1307                 switch (action) {
1308                         case SEL_SELECT:
1309                                 marker->flag |= SELECT;
1310                                 break;
1311                         case SEL_DESELECT:
1312                                 marker->flag &= ~SELECT;
1313                                 break;
1314                         case SEL_INVERT:
1315                                 marker->flag ^= SELECT;
1316                                 break;
1317                 }
1318         }
1319         
1320         WM_event_add_notifier(C, NC_SCENE | ND_MARKERS, NULL);
1321         WM_event_add_notifier(C, NC_ANIMATION | ND_MARKERS, NULL);
1322
1323         return OPERATOR_FINISHED;
1324 }
1325
1326 static void MARKER_OT_select_all(wmOperatorType *ot)
1327 {
1328         /* identifiers */
1329         ot->name = "(De)select all Markers";
1330         ot->description = "Change selection of all time markers";
1331         ot->idname = "MARKER_OT_select_all";
1332         
1333         /* api callbacks */
1334         ot->exec = ed_marker_select_all_exec;
1335         ot->invoke = ed_markers_opwrap_invoke;
1336         ot->poll = ed_markers_poll_markers_exist;
1337         
1338         /* flags */
1339         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1340         
1341         /* rna */
1342         WM_operator_properties_select_all(ot);
1343 }
1344
1345 /* ***************** remove marker *********************** */
1346
1347 /* remove selected TimeMarkers */
1348 static int ed_marker_delete_exec(bContext *C, wmOperator *UNUSED(op))
1349 {
1350         ListBase *markers = ED_context_get_markers(C);
1351         TimeMarker *marker, *nmarker;
1352         bool changed = false;
1353         
1354         if (markers == NULL)
1355                 return OPERATOR_CANCELLED;
1356         
1357         for (marker = markers->first; marker; marker = nmarker) {
1358                 nmarker = marker->next;
1359                 if (marker->flag & SELECT) {
1360                         BLI_freelinkN(markers, marker);
1361                         changed = true;
1362                 }
1363         }
1364         
1365         if (changed) {
1366                 WM_event_add_notifier(C, NC_SCENE | ND_MARKERS, NULL);
1367                 WM_event_add_notifier(C, NC_ANIMATION | ND_MARKERS, NULL);
1368         }
1369         
1370         return OPERATOR_FINISHED;
1371 }
1372
1373 static int ed_marker_delete_invoke_wrapper(bContext *C, wmOperator *op, const wmEvent *event)
1374 {
1375         // XXX: must we keep these confirmations?
1376         return ed_markers_opwrap_invoke_custom(C, op, event, WM_operator_confirm);
1377 }
1378
1379 static void MARKER_OT_delete(wmOperatorType *ot)
1380 {
1381         /* identifiers */
1382         ot->name = "Delete Markers";
1383         ot->description = "Delete selected time marker(s)";
1384         ot->idname = "MARKER_OT_delete";
1385         
1386         /* api callbacks */
1387         ot->invoke = ed_marker_delete_invoke_wrapper;
1388         ot->exec = ed_marker_delete_exec;
1389         ot->poll = ed_markers_poll_selected_no_locked_markers;
1390         
1391         /* flags */
1392         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1393 }
1394
1395
1396 /* **************** rename marker ***************** */
1397
1398 /* rename first selected TimeMarker */
1399 static int ed_marker_rename_exec(bContext *C, wmOperator *op)
1400 {
1401         TimeMarker *marker = ED_markers_get_first_selected(ED_context_get_markers(C));
1402
1403         if (marker) {
1404                 RNA_string_get(op->ptr, "name", marker->name);
1405                 
1406                 WM_event_add_notifier(C, NC_SCENE | ND_MARKERS, NULL);
1407                 WM_event_add_notifier(C, NC_ANIMATION | ND_MARKERS, NULL);
1408                 
1409                 return OPERATOR_FINISHED;
1410         }
1411         else {
1412                 return OPERATOR_CANCELLED;
1413         }
1414 }
1415
1416 static int ed_marker_rename_invoke_wrapper(bContext *C, wmOperator *op, const wmEvent *event)
1417 {
1418         /* must initialize the marker name first if there is a marker selected */
1419         TimeMarker *marker = ED_markers_get_first_selected(ED_context_get_markers(C));
1420         if (marker)
1421                 RNA_string_set(op->ptr, "name", marker->name);
1422         
1423         /* now see if the operator is usable */
1424         return ed_markers_opwrap_invoke_custom(C, op, event, WM_operator_props_popup_confirm);
1425 }
1426
1427 static void MARKER_OT_rename(wmOperatorType *ot)
1428 {
1429         /* identifiers */
1430         ot->name = "Rename Marker";
1431         ot->description = "Rename first selected time marker";
1432         ot->idname = "MARKER_OT_rename";
1433         
1434         /* api callbacks */
1435         ot->invoke = ed_marker_rename_invoke_wrapper;
1436         ot->exec = ed_marker_rename_exec;
1437         ot->poll = ed_markers_poll_selected_no_locked_markers;
1438         
1439         /* flags */
1440         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1441         
1442         /* properties */
1443         ot->prop = RNA_def_string(ot->srna, "name", "RenamedMarker", sizeof(((TimeMarker *)NULL)->name), "Name", "New name for marker");
1444         //RNA_def_boolean(ot->srna, "ensure_unique", 0, "Ensure Unique", "Ensure that new name is unique within collection of markers");
1445 }
1446
1447 /* **************** make links to scene ***************** */
1448
1449 static int ed_marker_make_links_scene_exec(bContext *C, wmOperator *op)
1450 {
1451         ListBase *markers = ED_context_get_markers(C);
1452         Scene *scene_to = BLI_findlink(&CTX_data_main(C)->scene, RNA_enum_get(op->ptr, "scene"));
1453         TimeMarker *marker, *marker_new;
1454
1455         if (scene_to == NULL) {
1456                 BKE_report(op->reports, RPT_ERROR, "Scene not found");
1457                 return OPERATOR_CANCELLED;
1458         }
1459
1460         if (scene_to == CTX_data_scene(C)) {
1461                 BKE_report(op->reports, RPT_ERROR, "Cannot re-link markers into the same scene");
1462                 return OPERATOR_CANCELLED;
1463         }
1464
1465         if (scene_to->toolsettings->lock_markers) {
1466                 BKE_report(op->reports, RPT_ERROR, "Target scene has locked markers");
1467                 return OPERATOR_CANCELLED;
1468         }
1469
1470         /* copy markers */
1471         for (marker = markers->first; marker; marker = marker->next) {
1472                 if (marker->flag & SELECT) {
1473                         marker_new = MEM_dupallocN(marker);
1474                         marker_new->prev = marker_new->next = NULL;
1475                         
1476                         BLI_addtail(&scene_to->markers, marker_new);
1477                 }
1478         }
1479
1480         return OPERATOR_FINISHED;
1481 }
1482
1483 static int ed_marker_make_links_scene_invoke_wrapper(bContext *C, wmOperator *op, const wmEvent *event)
1484 {
1485         return ed_markers_opwrap_invoke_custom(C, op, event, WM_menu_invoke);
1486 }
1487
1488 static void MARKER_OT_make_links_scene(wmOperatorType *ot)
1489 {
1490         PropertyRNA *prop;
1491
1492         /* identifiers */
1493         ot->name = "Make Links to Scene";
1494         ot->description = "Copy selected markers to another scene";
1495         ot->idname = "MARKER_OT_make_links_scene";
1496
1497         /* api callbacks */
1498         ot->exec = ed_marker_make_links_scene_exec;
1499         ot->invoke = ed_marker_make_links_scene_invoke_wrapper;
1500         ot->poll = ed_markers_poll_selected_markers;
1501
1502         /* flags */
1503         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1504
1505         /* properties */
1506         prop = RNA_def_enum(ot->srna, "scene", DummyRNA_NULL_items, 0, "Scene", "");
1507         RNA_def_enum_funcs(prop, RNA_scene_itemf);
1508         RNA_def_property_flag(prop, PROP_ENUM_NO_TRANSLATE);
1509         ot->prop = prop;
1510
1511 }
1512
1513 #ifdef DURIAN_CAMERA_SWITCH
1514 /* ******************************* camera bind marker ***************** */
1515
1516 static int ed_marker_camera_bind_exec(bContext *C, wmOperator *UNUSED(op))
1517 {
1518         bScreen *sc = CTX_wm_screen(C);
1519         Scene *scene = CTX_data_scene(C);
1520         Object *ob = CTX_data_active_object(C);
1521         ListBase *markers = ED_context_get_markers(C);
1522         TimeMarker *marker;
1523
1524         marker = ED_markers_get_first_selected(markers);
1525         if (marker == NULL)
1526                 return OPERATOR_CANCELLED;
1527
1528         marker->camera = ob;
1529
1530         /* camera may have changes */
1531         BKE_scene_camera_switch_update(scene);
1532         BKE_screen_view3d_scene_sync(sc);
1533
1534         WM_event_add_notifier(C, NC_SCENE | ND_MARKERS, NULL);
1535         WM_event_add_notifier(C, NC_ANIMATION | ND_MARKERS, NULL);
1536         WM_event_add_notifier(C, NC_SCENE | NA_EDITED, scene); /* so we get view3d redraws */
1537
1538         return OPERATOR_FINISHED;
1539 }
1540
1541 static void MARKER_OT_camera_bind(wmOperatorType *ot)
1542 {
1543         /* identifiers */
1544         ot->name = "Bind Camera to Markers";
1545         ot->description = "Bind the active camera to selected marker(s)";
1546         ot->idname = "MARKER_OT_camera_bind";
1547
1548         /* api callbacks */
1549         ot->exec = ed_marker_camera_bind_exec;
1550         ot->invoke = ed_markers_opwrap_invoke;
1551         ot->poll = ed_markers_poll_selected_no_locked_markers;
1552
1553         /* flags */
1554         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1555 }
1556 #endif
1557
1558 /* ************************** registration **********************************/
1559
1560 /* called in screen_ops.c:ED_operatortypes_screen() */
1561 void ED_operatortypes_marker(void)
1562 {
1563         WM_operatortype_append(MARKER_OT_add);
1564         WM_operatortype_append(MARKER_OT_move);
1565         WM_operatortype_append(MARKER_OT_duplicate);
1566         WM_operatortype_append(MARKER_OT_select);
1567         WM_operatortype_append(MARKER_OT_select_border);
1568         WM_operatortype_append(MARKER_OT_select_all);
1569         WM_operatortype_append(MARKER_OT_delete);
1570         WM_operatortype_append(MARKER_OT_rename);
1571         WM_operatortype_append(MARKER_OT_make_links_scene);
1572 #ifdef DURIAN_CAMERA_SWITCH
1573         WM_operatortype_append(MARKER_OT_camera_bind);
1574 #endif
1575 }
1576
1577 /* called in screen_ops.c:ED_keymap_screen() */
1578 void ED_keymap_marker(wmKeyConfig *keyconf)
1579 {
1580         wmKeyMap *keymap = WM_keymap_find(keyconf, "Markers", 0, 0);
1581         wmKeyMapItem *kmi;
1582         
1583         WM_keymap_verify_item(keymap, "MARKER_OT_add", MKEY, KM_PRESS, 0, 0);
1584         WM_keymap_verify_item(keymap, "MARKER_OT_move", EVT_TWEAK_S, KM_ANY, 0, 0);
1585         WM_keymap_verify_item(keymap, "MARKER_OT_duplicate", DKEY, KM_PRESS, KM_SHIFT, 0);
1586         WM_keymap_verify_item(keymap, "MARKER_OT_select", SELECTMOUSE, KM_PRESS, 0, 0);
1587         kmi = WM_keymap_add_item(keymap, "MARKER_OT_select", SELECTMOUSE, KM_PRESS, KM_SHIFT, 0);
1588         RNA_boolean_set(kmi->ptr, "extend", true);
1589
1590 #ifdef DURIAN_CAMERA_SWITCH
1591         kmi = WM_keymap_add_item(keymap, "MARKER_OT_select", SELECTMOUSE, KM_PRESS, KM_CTRL, 0);
1592         RNA_boolean_set(kmi->ptr, "extend", false);
1593         RNA_boolean_set(kmi->ptr, "camera", true);
1594
1595         kmi = WM_keymap_add_item(keymap, "MARKER_OT_select", SELECTMOUSE, KM_PRESS, KM_SHIFT | KM_CTRL, 0);
1596         RNA_boolean_set(kmi->ptr, "extend", true);
1597         RNA_boolean_set(kmi->ptr, "camera", true);
1598 #else
1599         (void)kmi;
1600 #endif
1601         
1602         WM_keymap_verify_item(keymap, "MARKER_OT_select_border", BKEY, KM_PRESS, 0, 0);
1603         WM_keymap_verify_item(keymap, "MARKER_OT_select_all", AKEY, KM_PRESS, 0, 0);
1604         WM_keymap_add_item(keymap, "MARKER_OT_delete", XKEY, KM_PRESS, 0, 0);
1605         WM_keymap_add_item(keymap, "MARKER_OT_delete", DELKEY, KM_PRESS, 0, 0);
1606         WM_keymap_verify_item(keymap, "MARKER_OT_rename", MKEY, KM_PRESS, KM_CTRL, 0);
1607         
1608         WM_keymap_add_item(keymap, "MARKER_OT_move", GKEY, KM_PRESS, 0, 0);
1609 #ifdef DURIAN_CAMERA_SWITCH
1610         WM_keymap_add_item(keymap, "MARKER_OT_camera_bind", BKEY, KM_PRESS, KM_CTRL, 0);
1611 #endif
1612 }
1613
1614 /* to be called from animation editor keymaps, see note below */
1615 void ED_marker_keymap_animedit_conflictfree(wmKeyMap *keymap)
1616 {
1617         /* duplicate of some marker-hotkeys but without the bounds checking
1618          * since these are handy to be able to do unrestricted and won't conflict
1619          * with primary function hotkeys (Usability tweak [#27469])
1620          */
1621         WM_keymap_add_item(keymap, "MARKER_OT_add", MKEY, KM_PRESS, 0, 0);
1622         WM_keymap_add_item(keymap, "MARKER_OT_rename", MKEY, KM_PRESS, KM_CTRL, 0);
1623 }