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