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