2.5
[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->idname= "MARKER_OT_add";
363         
364         /* api callbacks */
365         ot->exec= ed_marker_add;
366         ot->poll= ED_operator_areaactive;
367         
368         /* flags */
369         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
370 }
371
372 /* ************************** transform markers *************************** */
373
374
375 /* operator state vars used:  
376         frs: delta movement
377
378 functions:
379
380         init()   check selection, add customdata with old values and some lookups
381
382         apply()  do the actual movement
383
384         exit()  cleanup, send notifier
385
386     cancel() to escpae from modal
387
388 callbacks:
389
390         exec()  calls init, apply, exit 
391
392         invoke() calls init, adds modal handler
393
394         modal() accept modal events while doing it, ends with apply and exit, or cancel
395
396 */
397
398 typedef struct MarkerMove {
399         SpaceLink *slink;
400         ListBase *markers;
401         int event_type;         /* store invoke-event, to verify */
402         int *oldframe, evtx, firstx;
403 } MarkerMove;
404
405 /* copy selection to temp buffer */
406 /* return 0 if not OK */
407 static int ed_marker_move_init(bContext *C, wmOperator *op)
408 {
409         ListBase *markers= context_get_markers(C);
410         MarkerMove *mm;
411         TimeMarker *marker;
412         int totmark=0;
413         int a;
414
415         if(markers == NULL) return 0;
416         
417         for (marker= markers->first; marker; marker= marker->next)
418                 if (marker->flag & SELECT) totmark++;
419         
420         if (totmark==0) return 0;
421         
422         op->customdata= mm= MEM_callocN(sizeof(MarkerMove), "Markermove");
423         mm->slink= CTX_wm_space_data(C);
424         mm->markers= markers;
425         mm->oldframe= MEM_callocN(totmark*sizeof(int), "MarkerMove oldframe");
426         
427         for (a=0, marker= markers->first; marker; marker= marker->next) {
428                 if (marker->flag & SELECT) {
429                         mm->oldframe[a]= marker->frame;
430                         a++;
431                 }
432         }
433         
434         return 1;
435 }
436
437 /* free stuff */
438 static void ed_marker_move_exit(bContext *C, wmOperator *op)
439 {
440         MarkerMove *mm= op->customdata;
441         
442         /* free data */
443         MEM_freeN(mm->oldframe);
444         MEM_freeN(op->customdata);
445         op->customdata= NULL;
446         
447         /* clear custom header prints */
448         ED_area_headerprint(CTX_wm_area(C), NULL);
449 }
450
451 static int ed_marker_move_invoke(bContext *C, wmOperator *op, wmEvent *evt)
452 {
453         if(ed_marker_move_init(C, op)) {
454                 MarkerMove *mm= op->customdata;
455                 
456                 mm->evtx= evt->x;
457                 mm->firstx= evt->x;
458                 mm->event_type= evt->type;
459                 
460                 /* add temp handler */
461                 WM_event_add_modal_handler(C, &CTX_wm_window(C)->handlers, op);
462                 
463                 /* reset frs delta */
464                 RNA_int_set(op->ptr, "frames", 0);
465                 
466                 return OPERATOR_RUNNING_MODAL;
467         }
468         
469         return OPERATOR_CANCELLED;
470 }
471
472 /* note, init has to be called succesfully */
473 static void ed_marker_move_apply(bContext *C, wmOperator *op)
474 {
475         MarkerMove *mm= op->customdata;
476         TimeMarker *marker;
477         int a, offs;
478         
479         offs= RNA_int_get(op->ptr, "frames");
480         for (a=0, marker= mm->markers->first; marker; marker= marker->next) {
481                 if (marker->flag & SELECT) {
482                         marker->frame= mm->oldframe[a] + offs;
483                         a++;
484                 }
485         }
486 }
487
488 /* only for modal */
489 static void ed_marker_move_cancel(bContext *C, wmOperator *op)
490 {
491         RNA_int_set(op->ptr, "frames", 0);
492         ed_marker_move_apply(C, op);
493         ed_marker_move_exit(C, op);     
494         
495         WM_event_add_notifier(C, NC_SCENE|ND_MARKERS, NULL);
496 }
497
498
499
500 static int ed_marker_move_modal(bContext *C, wmOperator *op, wmEvent *evt)
501 {
502         Scene *scene= CTX_data_scene(C);
503         MarkerMove *mm= op->customdata;
504         View2D *v2d= UI_view2d_fromcontext(C);
505         TimeMarker *marker, *selmarker=NULL;
506         float dx, fac;
507         char str[256];
508                 
509         switch(evt->type) {
510                 case ESCKEY:
511                         ed_marker_move_cancel(C, op);
512                         return OPERATOR_CANCELLED;
513                 
514                 case LEFTMOUSE:
515                 case MIDDLEMOUSE:
516                 case RIGHTMOUSE:
517                         if(WM_modal_tweak_exit(evt, mm->event_type)) {
518                                 ed_marker_move_exit(C, op);
519                                 WM_event_add_notifier(C, NC_SCENE|ND_MARKERS, NULL);
520                                 return OPERATOR_FINISHED;
521                         }
522                         
523                         break;
524                 case MOUSEMOVE:
525                         dx= v2d->mask.xmax-v2d->mask.xmin;
526                         dx= (v2d->cur.xmax-v2d->cur.xmin)/dx;
527                         
528                         if (evt->x != mm->evtx) {       /* XXX maybe init for firsttime */
529                                 int a, offs, totmark=0;
530                                 
531                                 mm->evtx= evt->x;
532                                 
533                                 fac= ((float)(evt->x - mm->firstx)*dx);
534                                 
535                                 if (ELEM(mm->slink->spacetype, SPACE_TIME, SPACE_SOUND)) 
536                                         apply_keyb_grid(evt->shift, evt->ctrl, &fac, 0.0, FPS, 0.1*FPS, 0);
537                                 else
538                                         apply_keyb_grid(evt->shift, evt->ctrl, &fac, 0.0, 1.0, 0.1, U.flag & USER_AUTOGRABGRID);
539                                 
540                                 offs= (int)fac;
541                                 RNA_int_set(op->ptr, "frames", offs);
542                                 ed_marker_move_apply(C, op);
543                                 
544                                 /* cruft below is for header print */
545                                 for (a=0, marker= mm->markers->first; marker; marker= marker->next) {
546                                         if (marker->flag & SELECT) {
547                                                 selmarker= marker;
548                                                 a++; totmark++;
549                                         }
550                                 }
551                                 
552                                 if (totmark==1) {       
553                                         /* we print current marker value */
554                                         if (ELEM(mm->slink->spacetype, SPACE_TIME, SPACE_SOUND)) {
555                                                 SpaceTime *stime= (SpaceTime *)mm->slink;
556                                                 if (stime->flag & TIME_DRAWFRAMES) 
557                                                         sprintf(str, "Marker %d offset %d", selmarker->frame, offs);
558                                                 else 
559                                                         sprintf(str, "Marker %.2f offset %.2f", FRA2TIME(selmarker->frame), FRA2TIME(offs));
560                                         }
561                                         else if (mm->slink->spacetype == SPACE_ACTION) {
562                                                 SpaceAction *saction= (SpaceAction *)mm->slink;
563                                                 if (saction->flag & SACTION_DRAWTIME)
564                                                         sprintf(str, "Marker %.2f offset %.2f", FRA2TIME(selmarker->frame), FRA2TIME(offs));
565                                                 else
566                                                         sprintf(str, "Marker %.2f offset %.2f", (double)(selmarker->frame), (double)(offs));
567                                         }
568                                         else {
569                                                 sprintf(str, "Marker %.2f offset %.2f", (double)(selmarker->frame), (double)(offs));
570                                         }
571                                 }
572                                 else {
573                                         /* we only print the offset */
574                                         if (ELEM(mm->slink->spacetype, SPACE_TIME, SPACE_SOUND)) { 
575                                                 SpaceTime *stime= (SpaceTime *)mm->slink;
576                                                 if (stime->flag & TIME_DRAWFRAMES) 
577                                                         sprintf(str, "Marker offset %d ", offs);
578                                                 else 
579                                                         sprintf(str, "Marker offset %.2f ", FRA2TIME(offs));
580                                         }
581                                         else if (mm->slink->spacetype == SPACE_ACTION) {
582                                                 SpaceAction *saction= (SpaceAction *)mm->slink;
583                                                 if (saction->flag & SACTION_DRAWTIME)
584                                                         sprintf(str, "Marker offset %.2f ", FRA2TIME(offs));
585                                                 else
586                                                         sprintf(str, "Marker offset %.2f ", (double)(offs));
587                                         }
588                                         else {
589                                                 sprintf(str, "Marker offset %.2f ", (double)(offs));
590                                         }
591                                 }
592                                 
593                                 WM_event_add_notifier(C, NC_SCENE|ND_MARKERS, NULL);
594                                 ED_area_headerprint(CTX_wm_area(C), str);
595                         }
596         }
597
598         return OPERATOR_RUNNING_MODAL;
599 }
600
601 static int ed_marker_move_exec(bContext *C, wmOperator *op)
602 {
603         if(ed_marker_move_init(C, op)) {
604                 ed_marker_move_apply(C, op);
605                 ed_marker_move_exit(C, op);
606                 return OPERATOR_FINISHED;
607         }
608         return OPERATOR_PASS_THROUGH;
609 }
610
611 static void MARKER_OT_move(wmOperatorType *ot)
612 {
613         /* identifiers */
614         ot->name= "Move Time Marker";
615         ot->idname= "MARKER_OT_move";
616         
617         /* api callbacks */
618         ot->exec= ed_marker_move_exec;
619         ot->invoke= ed_marker_move_invoke;
620         ot->modal= ed_marker_move_modal;
621         ot->poll= ED_operator_areaactive;
622         
623         /* flags */
624         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO|OPTYPE_BLOCKING;
625         
626         /* rna storage */
627         RNA_def_int(ot->srna, "frames", 0, INT_MIN, INT_MAX, "Frames", "", INT_MIN, INT_MAX);
628 }
629
630 /* ************************** duplicate markers *************************** */
631
632 /* operator state vars used:  
633         frs: delta movement
634
635 functions:
636
637         apply()  do the actual duplicate
638
639 callbacks:
640
641         exec()  calls apply, move_exec
642
643         invoke() calls apply, move_invoke
644
645         modal() uses move_modal
646
647 */
648
649
650 /* duplicate selected TimeMarkers */
651 static void ed_marker_duplicate_apply(bContext *C, wmOperator *op)
652 {
653         ListBase *markers= context_get_markers(C);
654         TimeMarker *marker, *newmarker;
655         
656         if (markers == NULL) 
657                 return;
658
659         /* go through the list of markers, duplicate selected markers and add duplicated copies
660          * to the begining of the list (unselect original markers) 
661          */
662         for (marker= markers->first; marker; marker= marker->next) {
663                 if (marker->flag & SELECT) {
664                         /* unselect selected marker */
665                         marker->flag &= ~SELECT;
666                         
667                         /* create and set up new marker */
668                         newmarker = MEM_callocN(sizeof(TimeMarker), "TimeMarker");
669                         newmarker->flag= SELECT;
670                         newmarker->frame= marker->frame;
671                         BLI_strncpy(newmarker->name, marker->name, sizeof(marker->name));
672                         
673                         /* new marker is added to the begining of list */
674                         BLI_addhead(markers, newmarker);
675                 }
676         }
677 }
678
679 static int ed_marker_duplicate_exec(bContext *C, wmOperator *op)
680 {
681         ed_marker_duplicate_apply(C, op);
682         ed_marker_move_exec(C, op);     /* assumes frs delta set */
683         
684         return OPERATOR_FINISHED;
685         
686 }
687
688 static int ed_marker_duplicate_invoke(bContext *C, wmOperator *op, wmEvent *evt)
689 {
690         ed_marker_duplicate_apply(C, op);
691         return ed_marker_move_invoke(C, op, evt);
692 }
693
694 static void MARKER_OT_duplicate(wmOperatorType *ot)
695 {
696         /* identifiers */
697         ot->name= "Duplicate Time Marker";
698         ot->idname= "MARKER_OT_duplicate";
699         
700         /* api callbacks */
701         ot->exec= ed_marker_duplicate_exec;
702         ot->invoke= ed_marker_duplicate_invoke;
703         ot->modal= ed_marker_move_modal;
704         ot->poll= ED_operator_areaactive;
705         
706         /* flags */
707         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
708         
709         /* rna storage */
710         RNA_def_int(ot->srna, "frames", 0, INT_MIN, INT_MAX, "Frames", "", INT_MIN, INT_MAX);
711 }
712
713 /* ************************** selection ************************************/
714
715 /* select/deselect TimeMarker at current frame */
716 static void select_timeline_marker_frame(ListBase *markers, int frame, unsigned char shift)
717 {
718         TimeMarker *marker;
719         int select=0;
720         
721         for (marker= markers->first; marker; marker= marker->next) {
722                 /* if Shift is not set, then deselect Markers */
723                 if (!shift) marker->flag &= ~SELECT;
724                 
725                 /* this way a not-shift select will allways give 1 selected marker */
726                 if ((marker->frame == frame) && (!select)) {
727                         if (marker->flag & SELECT) 
728                                 marker->flag &= ~SELECT;
729                         else
730                                 marker->flag |= SELECT;
731                         select = 1;
732                 }
733         }
734 }
735
736 static int ed_marker_select(bContext *C, wmEvent *evt, int extend)
737 {
738         ListBase *markers= context_get_markers(C);
739         View2D *v2d= UI_view2d_fromcontext(C);
740         float viewx;
741         int x, y, cfra;
742         
743         if(markers == NULL)
744                 return OPERATOR_PASS_THROUGH;
745
746         x= evt->x - CTX_wm_region(C)->winrct.xmin;
747         y= evt->y - CTX_wm_region(C)->winrct.ymin;
748         
749         UI_view2d_region_to_view(v2d, x, y, &viewx, NULL);      
750         
751         cfra= ED_markers_find_nearest_marker_time(markers, viewx);
752         
753         if (extend)
754                 select_timeline_marker_frame(markers, cfra, 1);
755         else
756                 select_timeline_marker_frame(markers, cfra, 0);
757         
758         WM_event_add_notifier(C, NC_SCENE|ND_MARKERS, NULL);
759
760         /* allowing tweaks */
761         return OPERATOR_PASS_THROUGH;
762 }
763
764 static int ed_marker_select_invoke(bContext *C, wmOperator *op, wmEvent *evt)
765 {
766         short extend= RNA_boolean_get(op->ptr, "extend");
767         return ed_marker_select(C, evt, extend);
768 }
769
770 static void MARKER_OT_select(wmOperatorType *ot)
771 {
772         /* identifiers */
773         ot->name= "Select Time Marker";
774         ot->idname= "MARKER_OT_select";
775         
776         /* api callbacks */
777         ot->invoke= ed_marker_select_invoke;
778         ot->poll= ED_operator_areaactive;
779         
780         /* flags */
781         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
782
783         RNA_def_boolean(ot->srna, "extend", 0, "Extend", "extend the selection");
784 }
785
786 /* *************************** border select markers **************** */
787
788 /* operator state vars used: (added by default WM callbacks)   
789         xmin, ymin     
790         xmax, ymax     
791
792 customdata: the wmGesture pointer, with subwindow
793
794 callbacks:
795
796         exec()  has to be filled in by user
797
798         invoke() default WM function
799                         adds modal handler
800
801         modal() default WM function 
802                         accept modal events while doing it, calls exec(), handles ESC and border drawing
803
804         poll()  has to be filled in by user for context
805 */
806
807 static int ed_marker_border_select_exec(bContext *C, wmOperator *op)
808 {
809         View2D *v2d= UI_view2d_fromcontext(C);
810         ListBase *markers= context_get_markers(C);
811         TimeMarker *marker;
812         float xminf, xmaxf, yminf, ymaxf;
813         int event_type= RNA_int_get(op->ptr, "event_type");
814         int xmin= RNA_int_get(op->ptr, "xmin");
815         int xmax= RNA_int_get(op->ptr, "xmax");
816         int ymin= RNA_int_get(op->ptr, "ymin");
817         int ymax= RNA_int_get(op->ptr, "ymax");
818         
819         UI_view2d_region_to_view(v2d, xmin, ymin, &xminf, &yminf);      
820         UI_view2d_region_to_view(v2d, xmax, ymax, &xmaxf, &ymaxf);      
821         
822         /* XXX disputable */
823         if(yminf > 30.0f || ymaxf < 0.0f)
824                 return 0;
825         
826         if(markers == NULL)
827                 return 0;
828         
829         /* XXX marker context */
830         for(marker= markers->first; marker; marker= marker->next) {
831                 if ((marker->frame > xminf) && (marker->frame <= xmaxf)) {
832                         /* XXX weak... */
833                         switch (event_type) {
834                                 case LEFTMOUSE:
835                                         if ((marker->flag & SELECT) == 0) 
836                                                 marker->flag |= SELECT;
837                                         break;
838                                 case RIGHTMOUSE:
839                                         if (marker->flag & SELECT) 
840                                                 marker->flag &= ~SELECT;
841                                         break;
842                         }
843                 }
844         }
845         
846         WM_event_add_notifier(C, NC_SCENE|ND_MARKERS, NULL);
847
848         return 1;
849 }
850
851 static void MARKER_OT_select_border(wmOperatorType *ot)
852 {
853         /* identifiers */
854         ot->name= "Marker Border select";
855         ot->idname= "MARKER_OT_select_border";
856         
857         /* api callbacks */
858         ot->exec= ed_marker_border_select_exec;
859         ot->invoke= WM_border_select_invoke;
860         ot->modal= WM_border_select_modal;
861         
862         ot->poll= ED_operator_areaactive;
863         
864         /* flags */
865         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
866         
867         /* rna */
868         RNA_def_int(ot->srna, "event_type", 0, INT_MIN, INT_MAX, "Event Type", "", INT_MIN, INT_MAX);
869         RNA_def_int(ot->srna, "xmin", 0, INT_MIN, INT_MAX, "X Min", "", INT_MIN, INT_MAX);
870         RNA_def_int(ot->srna, "xmax", 0, INT_MIN, INT_MAX, "X Max", "", INT_MIN, INT_MAX);
871         RNA_def_int(ot->srna, "ymin", 0, INT_MIN, INT_MAX, "Y Min", "", INT_MIN, INT_MAX);
872         RNA_def_int(ot->srna, "ymax", 0, INT_MIN, INT_MAX, "Y Max", "", INT_MIN, INT_MAX);
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 select= RNA_int_get(op->ptr, "select_type");
882
883         if(markers == NULL)
884                 return OPERATOR_CANCELLED;
885         
886         if(RNA_boolean_get(op->ptr, "select_swap")) {
887                 for(marker= markers->first; marker; marker= marker->next) {
888                         if(marker->flag & SELECT)
889                                 break;
890                 }
891                 if(marker)
892                         select= 0;
893                 else
894                         select= 1;
895         }
896         
897         for(marker= markers->first; marker; marker= marker->next) {
898                 if(select)
899                         marker->flag |= SELECT;
900                 else
901                         marker->flag &= ~SELECT;
902         }
903         
904         WM_event_add_notifier(C, NC_SCENE|ND_MARKERS, NULL);
905
906         return OPERATOR_FINISHED;
907 }
908
909 static int ed_marker_select_all_invoke(bContext *C, wmOperator *op, wmEvent *evt)
910 {
911         RNA_boolean_set(op->ptr, "select_swap", 1);
912         
913         return ed_marker_select_all_exec(C, op);
914 }
915
916 static void MARKER_OT_select_all_toggle(wmOperatorType *ot)
917 {
918         /* identifiers */
919         ot->name= "(De)select all markers";
920         ot->idname= "MARKER_OT_select_all_toggle";
921         
922         /* api callbacks */
923         ot->exec= ed_marker_select_all_exec;
924         ot->invoke= ed_marker_select_all_invoke;
925         ot->poll= ED_operator_areaactive;
926         
927         /* flags */
928         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
929         
930         /* rna */
931         RNA_def_boolean(ot->srna, "select_swap", 0, "Select Swap", "");
932         RNA_def_int(ot->srna, "select_type", 0, INT_MIN, INT_MAX, "Select Type", "", INT_MIN, INT_MAX);
933 }
934
935 /* ******************************* remove marker ***************** */
936
937 /* remove selected TimeMarkers */
938 static int ed_marker_delete_exec(bContext *C, wmOperator *op)
939 {
940         ListBase *markers= context_get_markers(C);
941         TimeMarker *marker, *nmarker;
942         short changed= 0;
943         
944         if(markers == NULL)
945                 return OPERATOR_CANCELLED;
946         
947         for(marker= markers->first; marker; marker= nmarker) {
948                 nmarker= marker->next;
949                 if(marker->flag & SELECT) {
950                         BLI_freelinkN(markers, marker);
951                         changed= 1;
952                 }
953         }
954         
955         if (changed)
956                 WM_event_add_notifier(C, NC_SCENE|ND_MARKERS, NULL);
957         
958         return OPERATOR_FINISHED;
959 }
960
961
962 static void MARKER_OT_delete(wmOperatorType *ot)
963 {
964         /* identifiers */
965         ot->name= "Delete Markers";
966         ot->idname= "MARKER_OT_delete";
967         
968         /* api callbacks */
969         ot->invoke= WM_operator_confirm;
970         ot->exec= ed_marker_delete_exec;
971         ot->poll= ED_operator_areaactive;
972         
973         /* flags */
974         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
975         
976 }
977
978 /* ************************** registration **********************************/
979
980 /* called in screen_ops.c:ED_operatortypes_screen() */
981 void ED_operatortypes_marker(void)
982 {
983         WM_operatortype_append(MARKER_OT_add);
984         WM_operatortype_append(MARKER_OT_move);
985         WM_operatortype_append(MARKER_OT_duplicate);
986         WM_operatortype_append(MARKER_OT_select);
987         WM_operatortype_append(MARKER_OT_select_border);
988         WM_operatortype_append(MARKER_OT_select_all_toggle);
989         WM_operatortype_append(MARKER_OT_delete);
990 }
991
992 /* called in screen_ops.c:ED_keymap_screen() */
993 void ED_marker_keymap(wmWindowManager *wm)
994 {
995         ListBase *keymap= WM_keymap_listbase(wm, "Markers", 0, 0);
996         
997         WM_keymap_verify_item(keymap, "MARKER_OT_add", MKEY, KM_PRESS, 0, 0);
998         WM_keymap_verify_item(keymap, "MARKER_OT_move", EVT_TWEAK_S, KM_ANY, 0, 0);
999         WM_keymap_verify_item(keymap, "MARKER_OT_duplicate", DKEY, KM_PRESS, KM_SHIFT, 0);
1000         WM_keymap_verify_item(keymap, "MARKER_OT_select", SELECTMOUSE, KM_PRESS, 0, 0);
1001         RNA_boolean_set(WM_keymap_add_item(keymap, "MARKER_OT_select", SELECTMOUSE, KM_PRESS, KM_SHIFT, 0)->ptr, "extend", 1);
1002         WM_keymap_verify_item(keymap, "MARKER_OT_select_border", BKEY, KM_PRESS, 0, 0);
1003         WM_keymap_verify_item(keymap, "MARKER_OT_select_all_toggle", AKEY, KM_PRESS, 0, 0);
1004         WM_keymap_verify_item(keymap, "MARKER_OT_delete", XKEY, KM_PRESS, 0, 0);
1005         
1006         WM_keymap_add_item(keymap, "MARKER_OT_move", GKEY, KM_PRESS, 0, 0);
1007         
1008 }