Cleanup: rename draw_markers_time -> ED_markers_draw
[blender.git] / source / blender / editors / space_action / space_action.c
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version. 
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * The Original Code is Copyright (C) 2008 Blender Foundation.
19  * All rights reserved.
20  *
21  * 
22  * Contributor(s): Blender Foundation
23  *
24  * ***** END GPL LICENSE BLOCK *****
25  */
26
27 /** \file blender/editors/space_action/space_action.c
28  *  \ingroup spaction
29  */
30
31
32 #include <string.h>
33 #include <stdio.h>
34
35 #include "DNA_action_types.h"
36 #include "DNA_scene_types.h"
37
38 #include "MEM_guardedalloc.h"
39
40 #include "BLI_blenlib.h"
41 #include "BLI_math.h"
42 #include "BLI_utildefines.h"
43
44 #include "BKE_context.h"
45 #include "BKE_screen.h"
46
47 #include "BIF_gl.h"
48
49 #include "WM_api.h"
50 #include "WM_types.h"
51
52 #include "UI_resources.h"
53 #include "UI_view2d.h"
54
55 #include "ED_space_api.h"
56 #include "ED_screen.h"
57 #include "ED_anim_api.h"
58 #include "ED_markers.h"
59
60 #include "action_intern.h"  /* own include */
61
62 /* ******************** default callbacks for action space ***************** */
63
64 static SpaceLink *action_new(const bContext *C)
65 {
66         ScrArea *sa = CTX_wm_area(C);
67         SpaceAction *saction;
68         ARegion *ar;
69         
70         saction = MEM_callocN(sizeof(SpaceAction), "initaction");
71         saction->spacetype = SPACE_ACTION;
72         
73         saction->autosnap = SACTSNAP_FRAME;
74         saction->mode = SACTCONT_DOPESHEET;
75         
76         saction->ads.filterflag |= ADS_FILTER_SUMMARY;
77         
78         /* header */
79         ar = MEM_callocN(sizeof(ARegion), "header for action");
80         
81         BLI_addtail(&saction->regionbase, ar);
82         ar->regiontype = RGN_TYPE_HEADER;
83         ar->alignment = RGN_ALIGN_BOTTOM;
84         
85         /* channel list region */
86         ar = MEM_callocN(sizeof(ARegion), "channel area for action");
87         BLI_addtail(&saction->regionbase, ar);
88         ar->regiontype = RGN_TYPE_CHANNELS;
89         ar->alignment = RGN_ALIGN_LEFT;
90         
91         /* only need to set scroll settings, as this will use 'listview' v2d configuration */
92         ar->v2d.scroll = V2D_SCROLL_BOTTOM;
93         ar->v2d.flag = V2D_VIEWSYNC_AREA_VERTICAL;
94         
95         /* main area */
96         ar = MEM_callocN(sizeof(ARegion), "main area for action");
97         
98         BLI_addtail(&saction->regionbase, ar);
99         ar->regiontype = RGN_TYPE_WINDOW;
100         
101         ar->v2d.tot.xmin = -10.0f;
102         ar->v2d.tot.ymin = (float)(-sa->winy) / 3.0f;
103         ar->v2d.tot.xmax = (float)(sa->winx);
104         ar->v2d.tot.ymax = 0.0f;
105         
106         ar->v2d.cur = ar->v2d.tot;
107         
108         ar->v2d.min[0] = 0.0f;
109         ar->v2d.min[1] = 0.0f;
110         
111         ar->v2d.max[0] = MAXFRAMEF;
112         ar->v2d.max[1] = FLT_MAX;
113
114         ar->v2d.minzoom = 0.01f;
115         ar->v2d.maxzoom = 50;
116         ar->v2d.scroll = (V2D_SCROLL_BOTTOM | V2D_SCROLL_SCALE_HORIZONTAL);
117         ar->v2d.scroll |= (V2D_SCROLL_RIGHT);
118         ar->v2d.keepzoom = V2D_LOCKZOOM_Y;
119         ar->v2d.keepofs = V2D_KEEPOFS_Y;
120         ar->v2d.align = V2D_ALIGN_NO_POS_Y;
121         ar->v2d.flag = V2D_VIEWSYNC_AREA_VERTICAL;
122         
123         return (SpaceLink *)saction;
124 }
125
126 /* not spacelink itself */
127 static void action_free(SpaceLink *UNUSED(sl))
128 {       
129 //      SpaceAction *saction = (SpaceAction *) sl;
130 }
131
132
133 /* spacetype; init callback */
134 static void action_init(struct wmWindowManager *UNUSED(wm), ScrArea *sa)
135 {
136         SpaceAction *saction = sa->spacedata.first;
137         saction->flag |= SACTION_TEMP_NEEDCHANSYNC;
138 }
139
140 static SpaceLink *action_duplicate(SpaceLink *sl)
141 {
142         SpaceAction *sactionn = MEM_dupallocN(sl);
143         
144         /* clear or remove stuff from old */
145         
146         return (SpaceLink *)sactionn;
147 }
148
149
150
151 /* add handlers, stuff you only do once or on area/region changes */
152 static void action_main_area_init(wmWindowManager *wm, ARegion *ar)
153 {
154         wmKeyMap *keymap;
155         
156         UI_view2d_region_reinit(&ar->v2d, V2D_COMMONVIEW_CUSTOM, ar->winx, ar->winy);
157         
158         /* own keymap */
159         keymap = WM_keymap_find(wm->defaultconf, "Dopesheet", SPACE_ACTION, 0);
160         WM_event_add_keymap_handler_bb(&ar->handlers, keymap, &ar->v2d.mask, &ar->winrct);
161 }
162
163 static void action_main_area_draw(const bContext *C, ARegion *ar)
164 {
165         /* draw entirely, view changes should be handled here */
166         SpaceAction *saction = CTX_wm_space_action(C);
167         bAnimContext ac;
168         View2D *v2d = &ar->v2d;
169         View2DGrid *grid;
170         View2DScrollers *scrollers;
171         short unit = 0, flag = 0;
172         
173         /* clear and setup matrix */
174         UI_ThemeClearColor(TH_BACK);
175         glClear(GL_COLOR_BUFFER_BIT);
176         
177         UI_view2d_view_ortho(v2d);
178         
179         /* time grid */
180         unit = (saction->flag & SACTION_DRAWTIME) ? V2D_UNIT_SECONDS : V2D_UNIT_FRAMES;
181         grid = UI_view2d_grid_calc(CTX_data_scene(C), v2d, unit, V2D_GRID_CLAMP, V2D_ARG_DUMMY, V2D_ARG_DUMMY, ar->winx, ar->winy);
182         UI_view2d_grid_draw(v2d, grid, V2D_GRIDLINES_ALL);
183         UI_view2d_grid_free(grid);
184         
185         ED_region_draw_cb_draw(C, ar, REGION_DRAW_PRE_VIEW);
186
187         /* data */
188         if (ANIM_animdata_get_context(C, &ac)) {
189                 draw_channel_strips(&ac, saction, ar);
190         }
191         
192         /* current frame */
193         if (saction->flag & SACTION_DRAWTIME) flag |= DRAWCFRA_UNIT_SECONDS;
194         if ((saction->flag & SACTION_NODRAWCFRANUM) == 0) flag |= DRAWCFRA_SHOW_NUMBOX;
195         ANIM_draw_cfra(C, v2d, flag);
196         
197         /* markers */
198         UI_view2d_view_orthoSpecial(ar, v2d, 1);
199         
200         flag = ((ac.markers && (ac.markers != &ac.scene->markers)) ? DRAW_MARKERS_LOCAL : 0) | DRAW_MARKERS_MARGIN;
201         ED_markers_draw(C, flag);
202         
203         /* preview range */
204         UI_view2d_view_ortho(v2d);
205         ANIM_draw_previewrange(C, v2d, 0);
206
207         /* callback */
208         UI_view2d_view_ortho(v2d);
209         ED_region_draw_cb_draw(C, ar, REGION_DRAW_POST_VIEW);
210         
211         /* reset view matrix */
212         UI_view2d_view_restore(C);
213         
214         /* scrollers */
215         scrollers = UI_view2d_scrollers_calc(C, v2d, unit, V2D_GRID_CLAMP, V2D_ARG_DUMMY, V2D_ARG_DUMMY);
216         UI_view2d_scrollers_draw(C, v2d, scrollers);
217         UI_view2d_scrollers_free(scrollers);
218 }
219
220 /* add handlers, stuff you only do once or on area/region changes */
221 static void action_channel_area_init(wmWindowManager *wm, ARegion *ar)
222 {
223         wmKeyMap *keymap;
224         
225         /* ensure the 2d view sync works - main region has bottom scroller */
226         ar->v2d.scroll = V2D_SCROLL_BOTTOM;
227         
228         UI_view2d_region_reinit(&ar->v2d, V2D_COMMONVIEW_LIST, ar->winx, ar->winy);
229         
230         /* own keymap */
231         keymap = WM_keymap_find(wm->defaultconf, "Animation Channels", 0, 0);
232         WM_event_add_keymap_handler_bb(&ar->handlers, keymap, &ar->v2d.mask, &ar->winrct);
233 }
234
235 static void action_channel_area_draw(const bContext *C, ARegion *ar)
236 {
237         /* draw entirely, view changes should be handled here */
238         bAnimContext ac;
239         View2D *v2d = &ar->v2d;
240         
241         /* clear and setup matrix */
242         UI_ThemeClearColor(TH_BACK);
243         glClear(GL_COLOR_BUFFER_BIT);
244         
245         UI_view2d_view_ortho(v2d);
246         
247         /* data */
248         if (ANIM_animdata_get_context(C, &ac)) {
249                 draw_channel_names((bContext *)C, &ac, ar);
250         }
251         
252         /* reset view matrix */
253         UI_view2d_view_restore(C);
254         
255         /* no scrollers here */
256 }
257
258
259 /* add handlers, stuff you only do once or on area/region changes */
260 static void action_header_area_init(wmWindowManager *UNUSED(wm), ARegion *ar)
261 {
262         ED_region_header_init(ar);
263 }
264
265 static void action_header_area_draw(const bContext *C, ARegion *ar)
266 {
267         ED_region_header(C, ar);
268 }
269
270 static void action_channel_area_listener(bScreen *UNUSED(sc), ScrArea *UNUSED(sa), ARegion *ar, wmNotifier *wmn)
271 {
272         /* context changes */
273         switch (wmn->category) {
274                 case NC_ANIMATION:
275                         ED_region_tag_redraw(ar);
276                         break;
277                 case NC_SCENE:
278                         switch (wmn->data) {
279                                 case ND_OB_ACTIVE:
280                                 case ND_FRAME:
281                                         ED_region_tag_redraw(ar);
282                                         break;
283                         }
284                         break;
285                 case NC_OBJECT:
286                         switch (wmn->data) {
287                                 case ND_BONE_ACTIVE:
288                                 case ND_BONE_SELECT:
289                                 case ND_KEYS:
290                                         ED_region_tag_redraw(ar);
291                                         break;
292                                 case ND_MODIFIER:
293                                         if (wmn->action == NA_RENAME)
294                                                 ED_region_tag_redraw(ar);
295                                         break;
296                         }
297                         break;
298                 case NC_ID:
299                         if (wmn->action == NA_RENAME)
300                                 ED_region_tag_redraw(ar);
301                         break;
302                 default:
303                         if (wmn->data == ND_KEYS)
304                                 ED_region_tag_redraw(ar);
305                         break;
306         }
307 }
308
309 static void action_main_area_listener(bScreen *UNUSED(sc), ScrArea *UNUSED(sa), ARegion *ar, wmNotifier *wmn)
310 {
311         /* context changes */
312         switch (wmn->category) {
313                 case NC_ANIMATION:
314                         ED_region_tag_redraw(ar);
315                         break;
316                 case NC_SCENE:
317                         switch (wmn->data) {
318                                 case ND_RENDER_OPTIONS:
319                                 case ND_OB_ACTIVE:
320                                 case ND_FRAME:
321                                 case ND_MARKERS:
322                                         ED_region_tag_redraw(ar);
323                                         break;
324                         }
325                         break;
326                 case NC_OBJECT:
327                         switch (wmn->data) {
328                                 case ND_TRANSFORM:
329                                         /* moving object shouldn't need to redraw action */
330                                         break;
331                                 case ND_BONE_ACTIVE:
332                                 case ND_BONE_SELECT:
333                                 case ND_KEYS:
334                                         ED_region_tag_redraw(ar);
335                                         break;
336                         }
337                         break;
338                 case NC_NODE:
339                         switch (wmn->action) {
340                                 case NA_EDITED:
341                                         ED_region_tag_redraw(ar);
342                                         break;
343                         }
344                         break;
345                 case NC_ID:
346                         if (wmn->action == NA_RENAME)
347                                 ED_region_tag_redraw(ar);
348                         break;
349                                 
350                 default:
351                         if (wmn->data == ND_KEYS)
352                                 ED_region_tag_redraw(ar);
353                         break;
354         }
355 }
356
357 /* editor level listener */
358 static void action_listener(bScreen *UNUSED(sc), ScrArea *sa, wmNotifier *wmn)
359 {
360         SpaceAction *saction = (SpaceAction *)sa->spacedata.first;
361         
362         /* context changes */
363         switch (wmn->category) {
364                 case NC_GPENCIL:
365                         if (wmn->action == NA_EDITED) {
366                                 /* only handle this event in GPencil mode for performance considerations */
367                                 if (saction->mode == SACTCONT_GPENCIL)
368                                         ED_area_tag_redraw(sa);
369                         }
370                         break;
371                 case NC_ANIMATION:
372                         /* for NLA tweakmode enter/exit, need complete refresh */
373                         if (wmn->data == ND_NLA_ACTCHANGE) {
374                                 saction->flag |= SACTION_TEMP_NEEDCHANSYNC;
375                                 ED_area_tag_refresh(sa);
376                         }
377                         /* autocolor only really needs to change when channels are added/removed, or previously hidden stuff appears 
378                          * (assume for now that if just adding these works, that will be fine)
379                          */
380                         else if (((wmn->data == ND_KEYFRAME) && ELEM(wmn->action, NA_ADDED, NA_REMOVED)) ||
381                                  ((wmn->data == ND_ANIMCHAN) && (wmn->action != NA_SELECTED)))
382                         {
383                                 ED_area_tag_refresh(sa);
384                         }
385                         /* for simple edits to the curve data though (or just plain selections), a simple redraw should work 
386                          * (see T39851 for an example of how this can go wrong)
387                          */
388                         else {
389                                 ED_area_tag_redraw(sa);
390                         }
391                         break;
392                 case NC_SCENE:
393                         switch (wmn->data) {
394                                 case ND_OB_ACTIVE:  /* selection changed, so force refresh to flush (needs flag set to do syncing) */
395                                 case ND_OB_SELECT:
396                                         saction->flag |= SACTION_TEMP_NEEDCHANSYNC;
397                                         ED_area_tag_refresh(sa);
398                                         break;
399                                         
400                                 default: /* just redrawing the view will do */
401                                         ED_area_tag_redraw(sa);
402                                         break;
403                         }
404                         break;
405                 case NC_OBJECT:
406                         switch (wmn->data) {
407                                 case ND_BONE_SELECT:    /* selection changed, so force refresh to flush (needs flag set to do syncing) */
408                                 case ND_BONE_ACTIVE:
409                                         saction->flag |= SACTION_TEMP_NEEDCHANSYNC;
410                                         ED_area_tag_refresh(sa);
411                                         break;
412                                 case ND_TRANSFORM:
413                                         /* moving object shouldn't need to redraw action */
414                                         break;
415                                 default: /* just redrawing the view will do */
416                                         ED_area_tag_redraw(sa);
417                                         break;
418                         }
419                         break;
420                 case NC_MASK:
421                         if (saction->mode == SACTCONT_MASK) {
422                                 switch (wmn->data) {
423                                         case ND_DATA:
424                                                 ED_area_tag_refresh(sa);
425                                                 ED_area_tag_redraw(sa);
426                                                 break;
427                                         default: /* just redrawing the view will do */
428                                                 ED_area_tag_redraw(sa);
429                                                 break;
430                                 }
431                         }
432                         break;
433                 case NC_NODE:
434                         if (wmn->action == NA_SELECTED) {
435                                 /* selection changed, so force refresh to flush (needs flag set to do syncing) */
436                                 saction->flag |= SACTION_TEMP_NEEDCHANSYNC;
437                                 ED_area_tag_refresh(sa);
438                         }
439                         break;
440                 case NC_SPACE:
441                         switch (wmn->data) {
442                                 case ND_SPACE_DOPESHEET:
443                                         ED_area_tag_redraw(sa);
444                                         break;
445                                 case ND_SPACE_CHANGED:
446                                         saction->flag |= SACTION_TEMP_NEEDCHANSYNC;
447                                         ED_area_tag_refresh(sa);
448                                         break;
449                         }
450                         break;
451                 case NC_WINDOW:
452                         if (saction->flag & SACTION_TEMP_NEEDCHANSYNC) {
453                                 /* force redraw/refresh after undo/redo - [#28962] */
454                                 ED_area_tag_refresh(sa);
455                         }
456                         break;
457         }
458 }
459
460 static void action_header_area_listener(bScreen *UNUSED(sc), ScrArea *UNUSED(sa), ARegion *ar, wmNotifier *wmn)
461 {
462         // SpaceAction *saction = (SpaceAction *)sa->spacedata.first;
463
464         /* context changes */
465         switch (wmn->category) {
466                 case NC_SCENE:
467                         switch (wmn->data) {
468                                 case ND_OB_ACTIVE:
469                                         ED_region_tag_redraw(ar);
470                                         break;
471                         }
472                         break;
473                 case NC_ID:
474                         if (wmn->action == NA_RENAME)
475                                 ED_region_tag_redraw(ar);
476                         break;
477                 case NC_ANIMATION:
478                         switch (wmn->data) {
479                                 case ND_ANIMCHAN: /* set of visible animchannels changed */
480                                         /* NOTE: for now, this should usually just mean that the filters changed 
481                                          *       It may be better if we had a dedicated flag for that though
482                                          */
483                                         ED_region_tag_redraw(ar);
484                                         break;
485                                         
486                                 case ND_KEYFRAME: /* new keyframed added -> active action may have changed */
487                                         //saction->flag |= SACTION_TEMP_NEEDCHANSYNC;
488                                         ED_region_tag_redraw(ar);
489                                         break;
490                         }
491                         break;
492         }
493
494 }
495
496 static void action_refresh(const bContext *C, ScrArea *sa)
497 {
498         SpaceAction *saction = (SpaceAction *)sa->spacedata.first;
499         
500         /* update the state of the animchannels in response to changes from the data they represent 
501          * NOTE: the temp flag is used to indicate when this needs to be done, and will be cleared once handled
502          */
503         if (saction->flag & SACTION_TEMP_NEEDCHANSYNC) {
504                 ARegion *ar;
505                 
506                 /* Perform syncing of channel state incl. selection
507                  * Active action setting also occurs here (as part of anim channel filtering in anim_filter.c)
508                  */
509                 ANIM_sync_animchannels_to_data(C);
510                 saction->flag &= ~SACTION_TEMP_NEEDCHANSYNC;
511                 
512                 /* Tag everything for redraw
513                  * - Regions (such as header) need to be manually tagged for redraw too
514                  *   or else they don't update [#28962]
515                  */
516                 ED_area_tag_redraw(sa);
517                 for (ar = sa->regionbase.first; ar; ar = ar->next)
518                         ED_region_tag_redraw(ar);
519         }
520         
521         /* region updates? */
522         // XXX re-sizing y-extents of tot should go here?
523 }
524
525 /* only called once, from space/spacetypes.c */
526 void ED_spacetype_action(void)
527 {
528         SpaceType *st = MEM_callocN(sizeof(SpaceType), "spacetype action");
529         ARegionType *art;
530         
531         st->spaceid = SPACE_ACTION;
532         strncpy(st->name, "Action", BKE_ST_MAXNAME);
533         
534         st->new = action_new;
535         st->free = action_free;
536         st->init = action_init;
537         st->duplicate = action_duplicate;
538         st->operatortypes = action_operatortypes;
539         st->keymap = action_keymap;
540         st->listener = action_listener;
541         st->refresh = action_refresh;
542         
543         /* regions: main window */
544         art = MEM_callocN(sizeof(ARegionType), "spacetype action region");
545         art->regionid = RGN_TYPE_WINDOW;
546         art->init = action_main_area_init;
547         art->draw = action_main_area_draw;
548         art->listener = action_main_area_listener;
549         art->keymapflag = ED_KEYMAP_VIEW2D | ED_KEYMAP_MARKERS | ED_KEYMAP_ANIMATION | ED_KEYMAP_FRAMES;
550
551         BLI_addhead(&st->regiontypes, art);
552         
553         /* regions: header */
554         art = MEM_callocN(sizeof(ARegionType), "spacetype action region");
555         art->regionid = RGN_TYPE_HEADER;
556         art->prefsizey = HEADERY;
557         art->keymapflag = ED_KEYMAP_UI | ED_KEYMAP_VIEW2D | ED_KEYMAP_FRAMES | ED_KEYMAP_HEADER;
558         
559         art->init = action_header_area_init;
560         art->draw = action_header_area_draw;
561         art->listener = action_header_area_listener;
562         
563         BLI_addhead(&st->regiontypes, art);
564         
565         /* regions: channels */
566         art = MEM_callocN(sizeof(ARegionType), "spacetype action region");
567         art->regionid = RGN_TYPE_CHANNELS;
568         art->prefsizex = 200;
569         art->keymapflag = ED_KEYMAP_UI | ED_KEYMAP_VIEW2D | ED_KEYMAP_FRAMES;
570         
571         art->init = action_channel_area_init;
572         art->draw = action_channel_area_draw;
573         art->listener = action_channel_area_listener;
574         
575         BLI_addhead(&st->regiontypes, art);
576         
577         
578         BKE_spacetype_register(st);
579 }
580