- sequence strips without scenes would crash on display
[blender.git] / source / blender / editors / animation / anim_markers.c
1 /**
2  * $Id:
3  *
4  * ***** BEGIN GPL LICENSE BLOCK *****
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version. 
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
19  *
20  * The Original Code is Copyright (C) 2008 Blender Foundation.
21  * All rights reserved.
22  *
23  * 
24  * Contributor(s): Blender Foundation
25  *
26  * ***** END GPL LICENSE BLOCK *****
27  */
28
29 #include <stdlib.h>
30 #include <math.h>
31
32 #include "MEM_guardedalloc.h"
33
34 #include "DNA_action_types.h"
35 #include "DNA_scene_types.h"
36 #include "DNA_screen_types.h"
37 #include "DNA_space_types.h"
38 #include "DNA_view2d_types.h"
39 #include "DNA_userdef_types.h"
40 #include "DNA_windowmanager_types.h"
41
42 #include "RNA_access.h"
43 #include "RNA_define.h"
44
45 #include "BLI_blenlib.h"
46
47 #include "BKE_context.h"
48 #include "BKE_global.h"
49 #include "BKE_fcurve.h"
50 #include "BKE_utildefines.h"
51
52 #include "WM_api.h"
53 #include "WM_types.h"
54
55 #include "BIF_gl.h"
56 #include "BIF_glutil.h"
57
58 #include "UI_interface.h"
59 #include "UI_interface_icons.h"
60 #include "UI_view2d.h"
61 #include "UI_resources.h"
62
63 #include "ED_markers.h"
64 #include "ED_screen.h"
65 #include "ED_types.h"
66 #include "ED_util.h"
67
68 /* ************* Marker API **************** */
69
70 static ListBase *context_get_markers(const bContext *C)
71 {
72         
73 #if 0
74         /* XXX get them from pose */
75         if ((slink->spacetype == SPACE_ACTION) && (saction->flag & SACTION_POSEMARKERS_MOVE)) {
76                 if (saction->action)
77                         markers= &saction->action->markers;
78                 else
79                         markers= NULL;
80         }
81         else
82 #endif
83         
84         return &CTX_data_scene(C)->markers;
85 }
86
87 /* Get the marker that is closest to this point */
88 /* XXX for select, the min_dist should be small */
89 TimeMarker *ED_markers_find_nearest_marker (ListBase *markers, float x) 
90 {
91         TimeMarker *marker, *nearest=NULL;
92         float dist, min_dist= 1000000;
93         
94         if (markers) {
95                 for (marker= markers->first; marker; marker= marker->next) {
96                         dist = ABS((float)marker->frame - x);
97                         
98                         if (dist < min_dist) {
99                                 min_dist= dist;
100                                 nearest= marker;
101                         }
102                 }
103         }
104         
105         return nearest;
106 }
107
108 /* Return the time of the marker that occurs on a frame closest to the given time */
109 int ED_markers_find_nearest_marker_time (ListBase *markers, float x)
110 {
111         TimeMarker *nearest= ED_markers_find_nearest_marker(markers, x);
112         return (nearest) ? (nearest->frame) : (int)floor(x + 0.5f);
113 }
114
115
116 void ED_markers_get_minmax (ListBase *markers, short sel, float *first, float *last)
117 {
118         TimeMarker *marker;
119         float min, max;
120         int selcount = 0;
121         
122         /* sanity check */
123         printf("markers = %p -  %p, %p \n", markers, markers->first, markers->last);
124         if (markers == NULL) {
125                 *first = 0.0f;
126                 *last = 0.0f;
127                 return;
128         }
129         
130         if (markers->first && markers->last) {
131                 TimeMarker *fm= markers->first;
132                 TimeMarker *lm= markers->last;
133                 
134                 min= (float)fm->frame;
135                 max= (float)lm->frame;
136         }
137         else {
138                 *first = 0.0f;
139                 *last = 0.0f;
140                 return;
141         }
142         
143         /* count how many markers are usable - see later */
144         if (sel) {
145                 for (marker= markers->first; marker; marker= marker->next) {
146                         if (marker->flag & SELECT)
147                                 selcount++;
148                 }
149         }
150         else
151                 selcount= BLI_countlist(markers);
152         
153         /* if only selected are to be considered, only consider the selected ones
154          * (optimisation for not searching list) 
155          */
156         if (selcount > 1) {
157                 for (marker= markers->first; marker; marker= marker->next) {
158                         if (sel) {
159                                 if (marker->flag & SELECT) {
160                                         if (marker->frame < min)
161                                                 min= (float)marker->frame;
162                                         if (marker->frame > max)
163                                                 max= (float)marker->frame;
164                                 }
165                         }
166                         else {
167                                 if (marker->frame < min)
168                                         min= (float)marker->frame;
169                                 if (marker->frame > max)
170                                         max= (float)marker->frame;
171                         }       
172                 }
173         }
174         
175         /* set the min/max values */
176         *first= min;
177         *last= max;
178 }
179
180 /* Adds a marker to list of cfra elems */
181 void add_marker_to_cfra_elem(ListBase *lb, TimeMarker *marker, short only_sel)
182 {
183         CfraElem *ce, *cen;
184         
185         /* should this one only be considered if it is selected? */
186         if ((only_sel) && ((marker->flag & SELECT)==0))
187                 return;
188         
189         /* insertion sort - try to find a previous cfra elem */
190         for (ce= lb->first; ce; ce= ce->next) {
191                 if (ce->cfra == marker->frame) {
192                         /* do because of double keys */
193                         if (marker->flag & SELECT) 
194                                 ce->sel= marker->flag;
195                         return;
196                 }
197                 else if (ce->cfra > marker->frame) break;
198         }       
199         
200         cen= MEM_callocN(sizeof(CfraElem), "add_to_cfra_elem"); 
201         if (ce) BLI_insertlinkbefore(lb, ce, cen);
202         else BLI_addtail(lb, cen);
203
204         cen->cfra= marker->frame;
205         cen->sel= marker->flag;
206 }
207
208 /* This function makes a list of all the markers. The only_sel
209  * argument is used to specify whether only the selected markers
210  * are added.
211  */
212 void ED_markers_make_cfra_list(ListBase *markers, ListBase *lb, short only_sel)
213 {
214         TimeMarker *marker;
215         
216         if (markers == NULL)
217                 return;
218         
219         for (marker= markers->first; marker; marker= marker->next)
220                 add_marker_to_cfra_elem(lb, marker, only_sel);
221 }
222
223 /* ************* Marker Drawing ************ */
224
225 /* function to draw markers */
226 static void draw_marker(View2D *v2d, TimeMarker *marker, int cfra, int flag)
227 {
228         float xpos, ypixels, xscale, yscale;
229         int icon_id= 0;
230         
231         xpos = marker->frame;
232         
233         /* no time correction for framelen! space is drawn with old values */
234         ypixels= v2d->mask.ymax-v2d->mask.ymin;
235         UI_view2d_getscale(v2d, &xscale, &yscale);
236         
237         glScalef(1.0f/xscale, 1.0f, 1.0f);
238         
239         glEnable(GL_BLEND);
240         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);                      
241         
242         /* vertical line - dotted */
243         // NOTE: currently only used for sequencer
244 #ifdef DURIAN_CAMERA_SWITCH
245         if (marker->camera || flag & DRAW_MARKERS_LINES) {
246 #else
247         if (flag & DRAW_MARKERS_LINES) {
248 #endif
249                 setlinestyle(3);
250                 
251                 if (marker->flag & SELECT)
252                         glColor4ub(255, 255, 255, 96);
253                 else
254                         glColor4ub(0, 0, 0, 96);
255
256                 glBegin(GL_LINES);
257                         glVertex2f((xpos*xscale)+0.5f, 12.0f);
258                         glVertex2f((xpos*xscale)+0.5f, 34.0f*yscale); /* a bit lazy but we know it cant be greater then 34 strips high */
259                 glEnd();
260                 
261                 setlinestyle(0);
262         }
263         
264         /* 5 px to offset icon to align properly, space / pixels corrects for zoom */
265         if (flag & DRAW_MARKERS_LOCAL) {
266                 icon_id= (marker->flag & ACTIVE) ? ICON_PMARKER_ACT : 
267                 (marker->flag & SELECT) ? ICON_PMARKER_SEL : 
268                 ICON_PMARKER;
269         }
270         else {
271                 icon_id= (marker->flag & SELECT) ? ICON_MARKER_HLT : 
272                 ICON_MARKER;
273         }
274         
275         UI_icon_draw(xpos*xscale-5.0f, 16.0f, icon_id);
276         
277         glBlendFunc(GL_ONE, GL_ZERO);
278         glDisable(GL_BLEND);
279         
280         /* and the marker name too, shifted slightly to the top-right */
281         if (marker->name && marker->name[0]) {
282                 float x, y;
283                 
284                 if (marker->flag & SELECT) {
285                         UI_ThemeColor(TH_TEXT_HI);
286                         x= xpos*xscale + 4.0f;
287                         y= (ypixels <= 39.0f)? (ypixels-10.0f) : 29.0f;
288                 }
289                 else {
290                         UI_ThemeColor(TH_TEXT);
291                         if((marker->frame <= cfra) && (marker->frame+5 > cfra)) {
292                                 x= xpos*xscale + 4.0f;
293                                 y= (ypixels <= 39.0f)? (ypixels - 10.0f) : 29.0f;
294                         }
295                         else {
296                                 x= xpos*xscale + 4.0f;
297                                 y= 17.0f;
298                         }
299                 }
300                 UI_DrawString(x, y, marker->name);
301         }
302         
303         glScalef(xscale, 1.0f, 1.0f);
304 }
305
306 /* Draw Scene-Markers in time window */
307 void draw_markers_time(const bContext *C, int flag)
308 {
309         ListBase *markers= context_get_markers(C);
310         View2D *v2d= UI_view2d_fromcontext(C);
311         TimeMarker *marker;
312         
313         if (markers == NULL)
314                 return;
315         
316         /* unselected markers are drawn at the first time */
317         for (marker= markers->first; marker; marker= marker->next) {
318                 if ((marker->flag & SELECT) == 0) 
319                         draw_marker(v2d, marker, CTX_data_scene(C)->r.cfra, flag);
320         }
321         
322         /* selected markers are drawn later */
323         for (marker= markers->first; marker; marker= marker->next) {
324                 if (marker->flag & SELECT) 
325                         draw_marker(v2d, marker, CTX_data_scene(C)->r.cfra, flag);
326         }
327 }
328
329 /* ************************** add markers *************************** */
330
331 /* add TimeMarker at curent frame */
332 static int ed_marker_add(bContext *C, wmOperator *op)
333 {
334         ListBase *markers= context_get_markers(C);
335         TimeMarker *marker;
336         int frame= CTX_data_scene(C)->r.cfra;
337         
338         if (markers == NULL)
339                 return OPERATOR_CANCELLED;
340         
341         /* two markers can't be at the same place */
342         for (marker= markers->first; marker; marker= marker->next) {
343                 if (marker->frame == frame) 
344                         return OPERATOR_CANCELLED;
345         }
346         
347         /* deselect all */
348         for(marker= markers->first; marker; marker= marker->next)
349                 marker->flag &= ~SELECT;
350         
351         marker = MEM_callocN(sizeof(TimeMarker), "TimeMarker");
352         marker->flag= SELECT;
353         marker->frame= frame;
354         sprintf(marker->name, "Frame %d", frame); // XXX - temp code only
355         BLI_addtail(markers, marker);
356         
357         WM_event_add_notifier(C, NC_SCENE|ND_MARKERS, NULL);
358         
359         return OPERATOR_FINISHED;
360 }
361
362 static void MARKER_OT_add(wmOperatorType *ot)
363 {
364         /* identifiers */
365         ot->name= "Add Time Marker";
366         ot->description= "Add a new time marker.";
367         ot->idname= "MARKER_OT_add";
368         
369         /* api callbacks */
370         ot->exec= ed_marker_add;
371         ot->poll= ED_operator_areaactive;
372         
373         /* flags */
374         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
375 }
376
377 /* ************************** transform markers *************************** */
378
379
380 /* operator state vars used:  
381         frs: delta movement
382
383 functions:
384
385         init()   check selection, add customdata with old values and some lookups
386
387         apply()  do the actual movement
388
389         exit()  cleanup, send notifier
390
391     cancel() to escpae from modal
392
393 callbacks:
394
395         exec()  calls init, apply, exit 
396
397         invoke() calls init, adds modal handler
398
399         modal() accept modal events while doing it, ends with apply and exit, or cancel
400
401 */
402
403 typedef struct MarkerMove {
404         SpaceLink *slink;
405         ListBase *markers;
406         int event_type;         /* store invoke-event, to verify */
407         int *oldframe, evtx, firstx;
408 } MarkerMove;
409
410 /* copy selection to temp buffer */
411 /* return 0 if not OK */
412 static int ed_marker_move_init(bContext *C, wmOperator *op)
413 {
414         ListBase *markers= context_get_markers(C);
415         MarkerMove *mm;
416         TimeMarker *marker;
417         int totmark=0;
418         int a;
419
420         if(markers == NULL) return 0;
421         
422         for (marker= markers->first; marker; marker= marker->next)
423                 if (marker->flag & SELECT) totmark++;
424         
425         if (totmark==0) return 0;
426         
427         op->customdata= mm= MEM_callocN(sizeof(MarkerMove), "Markermove");
428         mm->slink= CTX_wm_space_data(C);
429         mm->markers= markers;
430         mm->oldframe= MEM_callocN(totmark*sizeof(int), "MarkerMove oldframe");
431         
432         for (a=0, marker= markers->first; marker; marker= marker->next) {
433                 if (marker->flag & SELECT) {
434                         mm->oldframe[a]= marker->frame;
435                         a++;
436                 }
437         }
438         
439         return 1;
440 }
441
442 /* free stuff */
443 static void ed_marker_move_exit(bContext *C, wmOperator *op)
444 {
445         MarkerMove *mm= op->customdata;
446         
447         /* free data */
448         MEM_freeN(mm->oldframe);
449         MEM_freeN(op->customdata);
450         op->customdata= NULL;
451         
452         /* clear custom header prints */
453         ED_area_headerprint(CTX_wm_area(C), NULL);
454 }
455
456 static int ed_marker_move_invoke(bContext *C, wmOperator *op, wmEvent *evt)
457 {
458         if(ed_marker_move_init(C, op)) {
459                 MarkerMove *mm= op->customdata;
460                 
461                 mm->evtx= evt->x;
462                 mm->firstx= evt->x;
463                 mm->event_type= evt->type;
464                 
465                 /* add temp handler */
466                 WM_event_add_modal_handler(C, op);
467                 
468                 /* reset frs delta */
469                 RNA_int_set(op->ptr, "frames", 0);
470                 
471                 return OPERATOR_RUNNING_MODAL;
472         }
473         
474         return OPERATOR_CANCELLED;
475 }
476
477 /* note, init has to be called succesfully */
478 static void ed_marker_move_apply(bContext *C, wmOperator *op)
479 {
480         MarkerMove *mm= op->customdata;
481         TimeMarker *marker;
482         int a, offs;
483         
484         offs= RNA_int_get(op->ptr, "frames");
485         for (a=0, marker= mm->markers->first; marker; marker= marker->next) {
486                 if (marker->flag & SELECT) {
487                         marker->frame= mm->oldframe[a] + offs;
488                         a++;
489                 }
490         }
491 }
492
493 /* only for modal */
494 static void ed_marker_move_cancel(bContext *C, wmOperator *op)
495 {
496         RNA_int_set(op->ptr, "frames", 0);
497         ed_marker_move_apply(C, op);
498         ed_marker_move_exit(C, op);     
499         
500         WM_event_add_notifier(C, NC_SCENE|ND_MARKERS, NULL);
501 }
502
503
504
505 static int ed_marker_move_modal(bContext *C, wmOperator *op, wmEvent *evt)
506 {
507         Scene *scene= CTX_data_scene(C);
508         MarkerMove *mm= op->customdata;
509         View2D *v2d= UI_view2d_fromcontext(C);
510         TimeMarker *marker, *selmarker=NULL;
511         float dx, fac;
512         char str[256];
513                 
514         switch(evt->type) {
515                 case ESCKEY:
516                         ed_marker_move_cancel(C, op);
517                         return OPERATOR_CANCELLED;
518                 
519                 case LEFTMOUSE:
520                 case MIDDLEMOUSE:
521                 case RIGHTMOUSE:
522                         if(WM_modal_tweak_exit(evt, mm->event_type)) {
523                                 ed_marker_move_exit(C, op);
524                                 WM_event_add_notifier(C, NC_SCENE|ND_MARKERS, NULL);
525                                 return OPERATOR_FINISHED;
526                         }
527                         
528                         break;
529                 case MOUSEMOVE:
530                         dx= v2d->mask.xmax-v2d->mask.xmin;
531                         dx= (v2d->cur.xmax-v2d->cur.xmin)/dx;
532                         
533                         if (evt->x != mm->evtx) {       /* XXX maybe init for firsttime */
534                                 int a, offs, totmark=0;
535                                 
536                                 mm->evtx= evt->x;
537                                 
538                                 fac= ((float)(evt->x - mm->firstx)*dx);
539                                 
540                                 if (ELEM(mm->slink->spacetype, SPACE_TIME, SPACE_SOUND)) 
541                                         apply_keyb_grid(evt->shift, evt->ctrl, &fac, 0.0, FPS, 0.1*FPS, 0);
542                                 else
543                                         apply_keyb_grid(evt->shift, evt->ctrl, &fac, 0.0, 1.0, 0.1, U.flag & USER_AUTOGRABGRID);
544                                 
545                                 offs= (int)fac;
546                                 RNA_int_set(op->ptr, "frames", offs);
547                                 ed_marker_move_apply(C, op);
548                                 
549                                 /* cruft below is for header print */
550                                 for (a=0, marker= mm->markers->first; marker; marker= marker->next) {
551                                         if (marker->flag & SELECT) {
552                                                 selmarker= marker;
553                                                 a++; totmark++;
554                                         }
555                                 }
556                                 
557                                 if (totmark==1) {       
558                                         /* we print current marker value */
559                                         if (ELEM(mm->slink->spacetype, SPACE_TIME, SPACE_SOUND)) {
560                                                 SpaceTime *stime= (SpaceTime *)mm->slink;
561                                                 if (stime->flag & TIME_DRAWFRAMES) 
562                                                         sprintf(str, "Marker %d offset %d", selmarker->frame, offs);
563                                                 else 
564                                                         sprintf(str, "Marker %.2f offset %.2f", FRA2TIME(selmarker->frame), FRA2TIME(offs));
565                                         }
566                                         else if (mm->slink->spacetype == SPACE_ACTION) {
567                                                 SpaceAction *saction= (SpaceAction *)mm->slink;
568                                                 if (saction->flag & SACTION_DRAWTIME)
569                                                         sprintf(str, "Marker %.2f offset %.2f", FRA2TIME(selmarker->frame), FRA2TIME(offs));
570                                                 else
571                                                         sprintf(str, "Marker %.2f offset %.2f", (double)(selmarker->frame), (double)(offs));
572                                         }
573                                         else {
574                                                 sprintf(str, "Marker %.2f offset %.2f", (double)(selmarker->frame), (double)(offs));
575                                         }
576                                 }
577                                 else {
578                                         /* we only print the offset */
579                                         if (ELEM(mm->slink->spacetype, SPACE_TIME, SPACE_SOUND)) { 
580                                                 SpaceTime *stime= (SpaceTime *)mm->slink;
581                                                 if (stime->flag & TIME_DRAWFRAMES) 
582                                                         sprintf(str, "Marker offset %d ", offs);
583                                                 else 
584                                                         sprintf(str, "Marker offset %.2f ", FRA2TIME(offs));
585                                         }
586                                         else if (mm->slink->spacetype == SPACE_ACTION) {
587                                                 SpaceAction *saction= (SpaceAction *)mm->slink;
588                                                 if (saction->flag & SACTION_DRAWTIME)
589                                                         sprintf(str, "Marker offset %.2f ", FRA2TIME(offs));
590                                                 else
591                                                         sprintf(str, "Marker offset %.2f ", (double)(offs));
592                                         }
593                                         else {
594                                                 sprintf(str, "Marker offset %.2f ", (double)(offs));
595                                         }
596                                 }
597                                 
598                                 WM_event_add_notifier(C, NC_SCENE|ND_MARKERS, NULL);
599                                 ED_area_headerprint(CTX_wm_area(C), str);
600                         }
601         }
602
603         return OPERATOR_RUNNING_MODAL;
604 }
605
606 static int ed_marker_move_exec(bContext *C, wmOperator *op)
607 {
608         if(ed_marker_move_init(C, op)) {
609                 ed_marker_move_apply(C, op);
610                 ed_marker_move_exit(C, op);
611                 return OPERATOR_FINISHED;
612         }
613         return OPERATOR_PASS_THROUGH;
614 }
615
616 static void MARKER_OT_move(wmOperatorType *ot)
617 {
618         /* identifiers */
619         ot->name= "Move Time Marker";
620         ot->description= "Move selected time marker(s).";
621         ot->idname= "MARKER_OT_move";
622         
623         /* api callbacks */
624         ot->exec= ed_marker_move_exec;
625         ot->invoke= ed_marker_move_invoke;
626         ot->modal= ed_marker_move_modal;
627         ot->poll= ED_operator_areaactive;
628         
629         /* flags */
630         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO|OPTYPE_BLOCKING;
631         
632         /* rna storage */
633         RNA_def_int(ot->srna, "frames", 0, INT_MIN, INT_MAX, "Frames", "", INT_MIN, INT_MAX);
634 }
635
636 /* ************************** duplicate markers *************************** */
637
638 /* operator state vars used:  
639         frs: delta movement
640
641 functions:
642
643         apply()  do the actual duplicate
644
645 callbacks:
646
647         exec()  calls apply, move_exec
648
649         invoke() calls apply, move_invoke
650
651         modal() uses move_modal
652
653 */
654
655
656 /* duplicate selected TimeMarkers */
657 static void ed_marker_duplicate_apply(bContext *C, wmOperator *op)
658 {
659         ListBase *markers= context_get_markers(C);
660         TimeMarker *marker, *newmarker;
661         
662         if (markers == NULL) 
663                 return;
664
665         /* go through the list of markers, duplicate selected markers and add duplicated copies
666          * to the begining of the list (unselect original markers) 
667          */
668         for (marker= markers->first; marker; marker= marker->next) {
669                 if (marker->flag & SELECT) {
670                         /* unselect selected marker */
671                         marker->flag &= ~SELECT;
672                         
673                         /* create and set up new marker */
674                         newmarker = MEM_callocN(sizeof(TimeMarker), "TimeMarker");
675                         newmarker->flag= SELECT;
676                         newmarker->frame= marker->frame;
677                         BLI_strncpy(newmarker->name, marker->name, sizeof(marker->name));
678                         
679 #ifdef DURIAN_CAMERA_SWITCH
680                         newmarker->camera= marker->camera;
681 #endif
682
683                         /* new marker is added to the begining of list */
684                         BLI_addhead(markers, newmarker);
685                 }
686         }
687 }
688
689 static int ed_marker_duplicate_exec(bContext *C, wmOperator *op)
690 {
691         ed_marker_duplicate_apply(C, op);
692         ed_marker_move_exec(C, op);     /* assumes frs delta set */
693         
694         return OPERATOR_FINISHED;
695         
696 }
697
698 static int ed_marker_duplicate_invoke(bContext *C, wmOperator *op, wmEvent *evt)
699 {
700         ed_marker_duplicate_apply(C, op);
701         return ed_marker_move_invoke(C, op, evt);
702 }
703
704 static void MARKER_OT_duplicate(wmOperatorType *ot)
705 {
706         /* identifiers */
707         ot->name= "Duplicate Time Marker";
708         ot->description= "Duplicate selected time marker(s).";
709         ot->idname= "MARKER_OT_duplicate";
710         
711         /* api callbacks */
712         ot->exec= ed_marker_duplicate_exec;
713         ot->invoke= ed_marker_duplicate_invoke;
714         ot->modal= ed_marker_move_modal;
715         ot->poll= ED_operator_areaactive;
716         
717         /* flags */
718         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
719         
720         /* rna storage */
721         RNA_def_int(ot->srna, "frames", 0, INT_MIN, INT_MAX, "Frames", "", INT_MIN, INT_MAX);
722 }
723
724 /* ************************** selection ************************************/
725
726 /* select/deselect TimeMarker at current frame */
727 static void select_timeline_marker_frame(ListBase *markers, int frame, unsigned char shift)
728 {
729         TimeMarker *marker;
730         int select=0;
731         
732         for (marker= markers->first; marker; marker= marker->next) {
733                 /* if Shift is not set, then deselect Markers */
734                 if (!shift) marker->flag &= ~SELECT;
735                 
736                 /* this way a not-shift select will allways give 1 selected marker */
737                 if ((marker->frame == frame) && (!select)) {
738                         if (marker->flag & SELECT) 
739                                 marker->flag &= ~SELECT;
740                         else
741                                 marker->flag |= SELECT;
742                         select = 1;
743                 }
744         }
745 }
746
747 static int ed_marker_select(bContext *C, wmEvent *evt, int extend)
748 {
749         ListBase *markers= context_get_markers(C);
750         View2D *v2d= UI_view2d_fromcontext(C);
751         float viewx;
752         int x, y, cfra;
753         
754         if(markers == NULL)
755                 return OPERATOR_PASS_THROUGH;
756
757         x= evt->x - CTX_wm_region(C)->winrct.xmin;
758         y= evt->y - CTX_wm_region(C)->winrct.ymin;
759         
760         UI_view2d_region_to_view(v2d, x, y, &viewx, NULL);      
761         
762         cfra= ED_markers_find_nearest_marker_time(markers, viewx);
763         
764         if (extend)
765                 select_timeline_marker_frame(markers, cfra, 1);
766         else
767                 select_timeline_marker_frame(markers, cfra, 0);
768         
769         WM_event_add_notifier(C, NC_SCENE|ND_MARKERS, NULL);
770
771         /* allowing tweaks */
772         return OPERATOR_PASS_THROUGH;
773 }
774
775 static int ed_marker_select_invoke(bContext *C, wmOperator *op, wmEvent *evt)
776 {
777         short extend= RNA_boolean_get(op->ptr, "extend");
778         return ed_marker_select(C, evt, extend);
779 }
780
781 static void MARKER_OT_select(wmOperatorType *ot)
782 {
783         /* identifiers */
784         ot->name= "Select Time Marker";
785         ot->description= "Select time marker(s).";
786         ot->idname= "MARKER_OT_select";
787         
788         /* api callbacks */
789         ot->invoke= ed_marker_select_invoke;
790         ot->poll= ED_operator_areaactive;
791         
792         /* flags */
793         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
794
795         RNA_def_boolean(ot->srna, "extend", 0, "Extend", "extend the selection");
796 }
797
798 /* *************************** border select markers **************** */
799
800 /* operator state vars used: (added by default WM callbacks)   
801         xmin, ymin     
802         xmax, ymax     
803
804 customdata: the wmGesture pointer, with subwindow
805
806 callbacks:
807
808         exec()  has to be filled in by user
809
810         invoke() default WM function
811                         adds modal handler
812
813         modal() default WM function 
814                         accept modal events while doing it, calls exec(), handles ESC and border drawing
815
816         poll()  has to be filled in by user for context
817 */
818
819 static int ed_marker_border_select_exec(bContext *C, wmOperator *op)
820 {
821         View2D *v2d= UI_view2d_fromcontext(C);
822         ListBase *markers= context_get_markers(C);
823         TimeMarker *marker;
824         float xminf, xmaxf, yminf, ymaxf;
825         int gesture_mode= RNA_int_get(op->ptr, "gesture_mode");
826         int xmin= RNA_int_get(op->ptr, "xmin");
827         int xmax= RNA_int_get(op->ptr, "xmax");
828         int ymin= RNA_int_get(op->ptr, "ymin");
829         int ymax= RNA_int_get(op->ptr, "ymax");
830         
831         UI_view2d_region_to_view(v2d, xmin, ymin, &xminf, &yminf);      
832         UI_view2d_region_to_view(v2d, xmax, ymax, &xmaxf, &ymaxf);      
833         
834         /* XXX disputable */
835         if(yminf > 30.0f || ymaxf < 0.0f)
836                 return 0;
837         
838         if(markers == NULL)
839                 return 0;
840         
841         /* XXX marker context */
842         for(marker= markers->first; marker; marker= marker->next) {
843                 if ((marker->frame > xminf) && (marker->frame <= xmaxf)) {
844                         switch (gesture_mode) {
845                                 case GESTURE_MODAL_SELECT:
846                                         if ((marker->flag & SELECT) == 0) 
847                                                 marker->flag |= SELECT;
848                                         break;
849                                 case GESTURE_MODAL_DESELECT:
850                                         if (marker->flag & SELECT) 
851                                                 marker->flag &= ~SELECT;
852                                         break;
853                         }
854                 }
855         }
856         
857         WM_event_add_notifier(C, NC_SCENE|ND_MARKERS, NULL);
858
859         return 1;
860 }
861
862 static void MARKER_OT_select_border(wmOperatorType *ot)
863 {
864         /* identifiers */
865         ot->name= "Marker Border select";
866         ot->description= "Select all time markers using border selection.";
867         ot->idname= "MARKER_OT_select_border";
868         
869         /* api callbacks */
870         ot->exec= ed_marker_border_select_exec;
871         ot->invoke= WM_border_select_invoke;
872         ot->modal= WM_border_select_modal;
873         
874         ot->poll= ED_operator_areaactive;
875         
876         /* flags */
877         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
878         
879         /* rna */
880         WM_operator_properties_gesture_border(ot, FALSE);
881 }
882
883 /* *********************** (de)select all ***************** */
884
885 static int ed_marker_select_all_exec(bContext *C, wmOperator *op)
886 {
887         ListBase *markers= context_get_markers(C);
888         TimeMarker *marker;
889         int action = RNA_enum_get(op->ptr, "action");
890
891         if(markers == NULL)
892                 return OPERATOR_CANCELLED;
893
894         if (action == SEL_TOGGLE) {
895                 action = SEL_SELECT;
896                 for(marker= markers->first; marker; marker= marker->next) {
897                         if(marker->flag & SELECT) {
898                                 action = SEL_DESELECT;
899                                 break;
900                         }
901                 }
902         }
903         
904         for(marker= markers->first; marker; marker= marker->next) {
905                 switch (action) {
906                 case SEL_SELECT:
907                         marker->flag |= SELECT;
908                         break;
909                 case SEL_DESELECT:
910                         marker->flag &= ~SELECT;
911                         break;
912                 case SEL_INVERT:
913                         if (marker->flag & SELECT) {
914                                 marker->flag &= ~SELECT;
915                         } else {
916                                 marker->flag |= SELECT;
917                         }
918                         break;
919                 }
920         }
921         
922         WM_event_add_notifier(C, NC_SCENE|ND_MARKERS, NULL);
923
924         return OPERATOR_FINISHED;
925 }
926
927 static void MARKER_OT_select_all(wmOperatorType *ot)
928 {
929         /* identifiers */
930         ot->name= "(De)select all markers";
931         ot->description= "Change selection of all time markers.";
932         ot->idname= "MARKER_OT_select_all";
933         
934         /* api callbacks */
935         ot->exec= ed_marker_select_all_exec;
936         ot->poll= ED_operator_areaactive;
937         
938         /* flags */
939         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
940         
941         /* rna */
942         WM_operator_properties_select_all(ot);
943 }
944
945 /* ******************************* remove marker ***************** */
946
947 /* remove selected TimeMarkers */
948 static int ed_marker_delete_exec(bContext *C, wmOperator *op)
949 {
950         ListBase *markers= context_get_markers(C);
951         TimeMarker *marker, *nmarker;
952         short changed= 0;
953         
954         if(markers == NULL)
955                 return OPERATOR_CANCELLED;
956         
957         for(marker= markers->first; marker; marker= nmarker) {
958                 nmarker= marker->next;
959                 if(marker->flag & SELECT) {
960                         BLI_freelinkN(markers, marker);
961                         changed= 1;
962                 }
963         }
964         
965         if (changed)
966                 WM_event_add_notifier(C, NC_SCENE|ND_MARKERS, NULL);
967         
968         return OPERATOR_FINISHED;
969 }
970
971
972 static void MARKER_OT_delete(wmOperatorType *ot)
973 {
974         /* identifiers */
975         ot->name= "Delete Markers";
976         ot->description= "Delete selected time marker(s).";
977         ot->idname= "MARKER_OT_delete";
978         
979         /* api callbacks */
980         ot->invoke= WM_operator_confirm;
981         ot->exec= ed_marker_delete_exec;
982         ot->poll= ED_operator_areaactive;
983         
984         /* flags */
985         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
986         
987 }
988
989 #ifdef DURIAN_CAMERA_SWITCH
990 /* ******************************* camera bind marker ***************** */
991
992 /* remove selected TimeMarkers */
993 static int ed_marker_camera_bind_exec(bContext *C, wmOperator *op)
994 {
995         Scene *scene= CTX_data_scene(C);
996         ListBase *markers= context_get_markers(C);
997         TimeMarker *marker;
998         short changed= 0;
999
1000         if(markers == NULL)
1001                 return OPERATOR_CANCELLED;
1002
1003         for(marker= markers->first; marker; marker= marker->next) {
1004                 if(marker->flag & SELECT) {
1005                         marker->camera= scene->camera;
1006                 }
1007         }
1008
1009         if (changed)
1010                 WM_event_add_notifier(C, NC_SCENE|ND_MARKERS, NULL);
1011
1012         return OPERATOR_FINISHED;
1013 }
1014
1015 static void MARKER_OT_camera_bind(wmOperatorType *ot)
1016 {
1017         /* identifiers */
1018         ot->name= "Bind Camera to Markers";
1019         ot->description= "Bind the active camera to selected markers(s).";
1020         ot->idname= "MARKER_OT_camera_bind";
1021
1022         /* api callbacks */
1023         ot->exec= ed_marker_camera_bind_exec;
1024         ot->poll= ED_operator_areaactive;
1025
1026         /* flags */
1027         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
1028 }
1029 #endif
1030
1031 /* ************************** registration **********************************/
1032
1033 /* called in screen_ops.c:ED_operatortypes_screen() */
1034 void ED_operatortypes_marker(void)
1035 {
1036         WM_operatortype_append(MARKER_OT_add);
1037         WM_operatortype_append(MARKER_OT_move);
1038         WM_operatortype_append(MARKER_OT_duplicate);
1039         WM_operatortype_append(MARKER_OT_select);
1040         WM_operatortype_append(MARKER_OT_select_border);
1041         WM_operatortype_append(MARKER_OT_select_all);
1042         WM_operatortype_append(MARKER_OT_delete);
1043 #ifdef DURIAN_CAMERA_SWITCH
1044         WM_operatortype_append(MARKER_OT_camera_bind);
1045 #endif
1046 }
1047
1048 /* called in screen_ops.c:ED_keymap_screen() */
1049 void ED_marker_keymap(wmKeyConfig *keyconf)
1050 {
1051         wmKeyMap *keymap= WM_keymap_find(keyconf, "Markers", 0, 0);
1052         
1053         WM_keymap_verify_item(keymap, "MARKER_OT_add", MKEY, KM_PRESS, 0, 0);
1054         WM_keymap_verify_item(keymap, "MARKER_OT_move", EVT_TWEAK_S, KM_ANY, 0, 0);
1055         WM_keymap_verify_item(keymap, "MARKER_OT_duplicate", DKEY, KM_PRESS, KM_SHIFT, 0);
1056         WM_keymap_verify_item(keymap, "MARKER_OT_select", SELECTMOUSE, KM_PRESS, 0, 0);
1057         RNA_boolean_set(WM_keymap_add_item(keymap, "MARKER_OT_select", SELECTMOUSE, KM_PRESS, KM_SHIFT, 0)->ptr, "extend", 1);
1058         WM_keymap_verify_item(keymap, "MARKER_OT_select_border", BKEY, KM_PRESS, 0, 0);
1059         WM_keymap_verify_item(keymap, "MARKER_OT_select_all", AKEY, KM_PRESS, 0, 0);
1060         WM_keymap_verify_item(keymap, "MARKER_OT_delete", XKEY, KM_PRESS, 0, 0);
1061         WM_keymap_verify_item(keymap, "MARKER_OT_delete", DELKEY, KM_PRESS, 0, 0);
1062         
1063         WM_keymap_add_item(keymap, "MARKER_OT_move", GKEY, KM_PRESS, 0, 0);
1064 #ifdef DURIAN_CAMERA_SWITCH
1065         WM_keymap_add_item(keymap, "MARKER_OT_camera_bind", BKEY, KM_PRESS, KM_CTRL, 0);
1066 #endif
1067 }