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