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