61477f0730e2382bad259958611cee68b30e0f3b
[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 "ED_screen.h"
48
49 #include "BIF_gl.h"
50
51 #include "WM_api.h"
52 #include "WM_types.h"
53
54 #include "UI_resources.h"
55 #include "UI_view2d.h"
56
57 #include "ED_space_api.h"
58 #include "ED_screen.h"
59 #include "ED_anim_api.h"
60 #include "ED_markers.h"
61
62 #include "action_intern.h"  /* own include */
63
64 /* ******************** default callbacks for action space ***************** */
65
66 static SpaceLink *action_new(const bContext *C)
67 {
68         ScrArea *sa = CTX_wm_area(C);
69         SpaceAction *saction;
70         ARegion *ar;
71         
72         saction = MEM_callocN(sizeof(SpaceAction), "initaction");
73         saction->spacetype = SPACE_ACTION;
74         
75         saction->autosnap = SACTSNAP_FRAME;
76         saction->mode = SACTCONT_DOPESHEET;
77         
78         saction->ads.filterflag |= ADS_FILTER_SUMMARY;
79         
80         /* header */
81         ar = MEM_callocN(sizeof(ARegion), "header for action");
82         
83         BLI_addtail(&saction->regionbase, ar);
84         ar->regiontype = RGN_TYPE_HEADER;
85         ar->alignment = RGN_ALIGN_BOTTOM;
86         
87         /* channel list region */
88         ar = MEM_callocN(sizeof(ARegion), "channel area for action");
89         BLI_addtail(&saction->regionbase, ar);
90         ar->regiontype = RGN_TYPE_CHANNELS;
91         ar->alignment = RGN_ALIGN_LEFT;
92         
93         /* only need to set scroll settings, as this will use 'listview' v2d configuration */
94         ar->v2d.scroll = V2D_SCROLL_BOTTOM;
95         ar->v2d.flag = V2D_VIEWSYNC_AREA_VERTICAL;
96         
97         /* main area */
98         ar = MEM_callocN(sizeof(ARegion), "main area for action");
99         
100         BLI_addtail(&saction->regionbase, ar);
101         ar->regiontype = RGN_TYPE_WINDOW;
102         
103         ar->v2d.tot.xmin = -10.0f;
104         ar->v2d.tot.ymin = (float)(-sa->winy) / 3.0f;
105         ar->v2d.tot.xmax = (float)(sa->winx);
106         ar->v2d.tot.ymax = 0.0f;
107         
108         ar->v2d.cur = ar->v2d.tot;
109         
110         ar->v2d.min[0] = 0.0f;
111         ar->v2d.min[1] = 0.0f;
112         
113         ar->v2d.max[0] = MAXFRAMEF;
114         ar->v2d.max[1] = FLT_MAX;
115
116         ar->v2d.minzoom = 0.01f;
117         ar->v2d.maxzoom = 50;
118         ar->v2d.scroll = (V2D_SCROLL_BOTTOM | V2D_SCROLL_SCALE_HORIZONTAL);
119         ar->v2d.scroll |= (V2D_SCROLL_RIGHT);
120         ar->v2d.keepzoom = V2D_LOCKZOOM_Y;
121         ar->v2d.keepofs = V2D_KEEPOFS_Y;
122         ar->v2d.align = V2D_ALIGN_NO_POS_Y;
123         ar->v2d.flag = V2D_VIEWSYNC_AREA_VERTICAL;
124         
125         return (SpaceLink *)saction;
126 }
127
128 /* not spacelink itself */
129 static void action_free(SpaceLink *UNUSED(sl))
130 {       
131 //      SpaceAction *saction = (SpaceAction *) sl;
132 }
133
134
135 /* spacetype; init callback */
136 static void action_init(struct wmWindowManager *UNUSED(wm), ScrArea *sa)
137 {
138         SpaceAction *saction = sa->spacedata.first;
139         saction->flag |= SACTION_TEMP_NEEDCHANSYNC;
140 }
141
142 static SpaceLink *action_duplicate(SpaceLink *sl)
143 {
144         SpaceAction *sactionn = MEM_dupallocN(sl);
145         
146         /* clear or remove stuff from old */
147         
148         return (SpaceLink *)sactionn;
149 }
150
151
152
153 /* add handlers, stuff you only do once or on area/region changes */
154 static void action_main_area_init(wmWindowManager *wm, ARegion *ar)
155 {
156         wmKeyMap *keymap;
157         
158         UI_view2d_region_reinit(&ar->v2d, V2D_COMMONVIEW_CUSTOM, ar->winx, ar->winy);
159         
160         /* own keymap */
161         keymap = WM_keymap_find(wm->defaultconf, "Dopesheet", SPACE_ACTION, 0);
162         WM_event_add_keymap_handler_bb(&ar->handlers, keymap, &ar->v2d.mask, &ar->winrct);
163 }
164
165 static void action_main_area_draw(const bContext *C, ARegion *ar)
166 {
167         /* draw entirely, view changes should be handled here */
168         SpaceAction *saction = CTX_wm_space_action(C);
169         bAnimContext ac;
170         View2D *v2d = &ar->v2d;
171         View2DGrid *grid;
172         View2DScrollers *scrollers;
173         short unit = 0, flag = 0;
174         
175         /* clear and setup matrix */
176         UI_ThemeClearColor(TH_BACK);
177         glClear(GL_COLOR_BUFFER_BIT);
178         
179         UI_view2d_view_ortho(v2d);
180         
181         /* time grid */
182         unit = (saction->flag & SACTION_DRAWTIME) ? V2D_UNIT_SECONDS : V2D_UNIT_FRAMES;
183         grid = UI_view2d_grid_calc(CTX_data_scene(C), v2d, unit, V2D_GRID_CLAMP, V2D_ARG_DUMMY, V2D_ARG_DUMMY, ar->winx, ar->winy);
184         UI_view2d_grid_draw(v2d, grid, V2D_GRIDLINES_ALL);
185         UI_view2d_grid_free(grid);
186         
187         ED_region_draw_cb_draw(C, ar, REGION_DRAW_PRE_VIEW);
188
189         /* data */
190         if (ANIM_animdata_get_context(C, &ac)) {
191                 draw_channel_strips(&ac, saction, ar);
192         }
193         
194         /* current frame */
195         if (saction->flag & SACTION_DRAWTIME) flag |= DRAWCFRA_UNIT_SECONDS;
196         if ((saction->flag & SACTION_NODRAWCFRANUM) == 0) flag |= DRAWCFRA_SHOW_NUMBOX;
197         ANIM_draw_cfra(C, v2d, flag);
198         
199         /* markers */
200         UI_view2d_view_orthoSpecial(ar, v2d, 1);
201         
202         flag = (ac.markers && (ac.markers != &ac.scene->markers)) ? DRAW_MARKERS_LOCAL : 0;
203         draw_markers_time(C, flag);
204         
205         /* preview range */
206         UI_view2d_view_ortho(v2d);
207         ANIM_draw_previewrange(C, v2d);
208
209         /* callback */
210         UI_view2d_view_ortho(v2d);
211         ED_region_draw_cb_draw(C, ar, REGION_DRAW_POST_VIEW);
212         
213         /* reset view matrix */
214         UI_view2d_view_restore(C);
215         
216         /* scrollers */
217         scrollers = UI_view2d_scrollers_calc(C, v2d, unit, V2D_GRID_CLAMP, V2D_ARG_DUMMY, V2D_ARG_DUMMY);
218         UI_view2d_scrollers_draw(C, v2d, scrollers);
219         UI_view2d_scrollers_free(scrollers);
220 }
221
222 /* add handlers, stuff you only do once or on area/region changes */
223 static void action_channel_area_init(wmWindowManager *wm, ARegion *ar)
224 {
225         wmKeyMap *keymap;
226         
227         /* ensure the 2d view sync works - main region has bottom scroller */
228         ar->v2d.scroll = V2D_SCROLL_BOTTOM;
229         
230         UI_view2d_region_reinit(&ar->v2d, V2D_COMMONVIEW_LIST, ar->winx, ar->winy);
231         
232         /* own keymap */
233         keymap = WM_keymap_find(wm->defaultconf, "Animation Channels", 0, 0);
234         WM_event_add_keymap_handler_bb(&ar->handlers, keymap, &ar->v2d.mask, &ar->winrct);
235 }
236
237 static void action_channel_area_draw(const bContext *C, ARegion *ar)
238 {
239         /* draw entirely, view changes should be handled here */
240         bAnimContext ac;
241         View2D *v2d = &ar->v2d;
242         
243         /* clear and setup matrix */
244         UI_ThemeClearColor(TH_BACK);
245         glClear(GL_COLOR_BUFFER_BIT);
246         
247         UI_view2d_view_ortho(v2d);
248         
249         /* data */
250         if (ANIM_animdata_get_context(C, &ac)) {
251                 draw_channel_names((bContext *)C, &ac, ar);
252         }
253         
254         /* reset view matrix */
255         UI_view2d_view_restore(C);
256         
257         /* no scrollers here */
258 }
259
260
261 /* add handlers, stuff you only do once or on area/region changes */
262 static void action_header_area_init(wmWindowManager *UNUSED(wm), ARegion *ar)
263 {
264         ED_region_header_init(ar);
265 }
266
267 static void action_header_area_draw(const bContext *C, ARegion *ar)
268 {
269         ED_region_header(C, ar);
270 }
271
272 static void action_channel_area_listener(ARegion *ar, wmNotifier *wmn)
273 {
274         /* context changes */
275         switch (wmn->category) {
276                 case NC_ANIMATION:
277                         ED_region_tag_redraw(ar);
278                         break;
279                 case NC_SCENE:
280                         switch (wmn->data) {
281                                 case ND_OB_ACTIVE:
282                                 case ND_FRAME:
283                                         ED_region_tag_redraw(ar);
284                                         break;
285                         }
286                         break;
287                 case NC_OBJECT:
288                         switch (wmn->data) {
289                                 case ND_BONE_ACTIVE:
290                                 case ND_BONE_SELECT:
291                                 case ND_KEYS:
292                                         ED_region_tag_redraw(ar);
293                                         break;
294                                 case ND_MODIFIER:
295                                         if (wmn->action == NA_RENAME)
296                                                 ED_region_tag_redraw(ar);
297                                         break;
298                         }
299                         break;
300                 case NC_ID:
301                         if (wmn->action == NA_RENAME)
302                                 ED_region_tag_redraw(ar);
303                         break;
304                 default:
305                         if (wmn->data == ND_KEYS)
306                                 ED_region_tag_redraw(ar);
307         }
308 }
309
310 static void action_main_area_listener(ARegion *ar, wmNotifier *wmn)
311 {
312         /* context changes */
313         switch (wmn->category) {
314                 case NC_ANIMATION:
315                         ED_region_tag_redraw(ar);
316                         break;
317                 case NC_SCENE:
318                         switch (wmn->data) {
319                                 case ND_RENDER_OPTIONS:
320                                 case ND_OB_ACTIVE:
321                                 case ND_FRAME:
322                                 case ND_MARKERS:
323                                         ED_region_tag_redraw(ar);
324                                         break;
325                         }
326                         break;
327                 case NC_OBJECT:
328                         switch (wmn->data) {
329                                 case ND_TRANSFORM:
330                                         /* moving object shouldn't need to redraw action */
331                                         break;
332                                 case ND_BONE_ACTIVE:
333                                 case ND_BONE_SELECT:
334                                 case ND_KEYS:
335                                         ED_region_tag_redraw(ar);
336                                         break;
337                         }
338                         break;
339                 case NC_NODE:
340                         switch (wmn->action) {
341                                 case NA_EDITED:
342                                         ED_region_tag_redraw(ar);
343                                         break;
344                         }
345                         break;
346                 case NC_ID:
347                         if (wmn->action == NA_RENAME)
348                                 ED_region_tag_redraw(ar);
349                         break;
350                                 
351                 default:
352                         if (wmn->data == ND_KEYS)
353                                 ED_region_tag_redraw(ar);
354         }
355 }
356
357 /* editor level listener */
358 static void action_listener(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                         /* for selection changes of animation data, we can just redraw... otherwise autocolor might need to be done again */
378                         else if (ELEM(wmn->data, ND_KEYFRAME, ND_ANIMCHAN) && (wmn->action == NA_SELECTED))
379                                 ED_area_tag_redraw(sa);
380                         else
381                                 ED_area_tag_refresh(sa);
382                         break;
383                 case NC_SCENE:
384                         switch (wmn->data) {
385                                 case ND_OB_ACTIVE:  /* selection changed, so force refresh to flush (needs flag set to do syncing) */
386                                 case ND_OB_SELECT:
387                                         saction->flag |= SACTION_TEMP_NEEDCHANSYNC;
388                                         ED_area_tag_refresh(sa);
389                                         break;
390                                         
391                                 default: /* just redrawing the view will do */
392                                         ED_area_tag_redraw(sa);
393                                         break;
394                         }
395                         break;
396                 case NC_OBJECT:
397                         switch (wmn->data) {
398                                 case ND_BONE_SELECT:    /* selection changed, so force refresh to flush (needs flag set to do syncing) */
399                                 case ND_BONE_ACTIVE:
400                                         saction->flag |= SACTION_TEMP_NEEDCHANSYNC;
401                                         ED_area_tag_refresh(sa);
402                                         break;
403                                 case ND_TRANSFORM:
404                                         /* moving object shouldn't need to redraw action */
405                                         break;
406                                 default: /* just redrawing the view will do */
407                                         ED_area_tag_redraw(sa);
408                                         break;
409                         }
410                         break;
411                 case NC_MASK:
412                         if (saction->mode == SACTCONT_MASK) {
413                                 switch (wmn->data) {
414                                         case ND_DATA:
415                                                 ED_area_tag_refresh(sa);
416                                                 ED_area_tag_redraw(sa);
417                                                 break;
418                                         default: /* just redrawing the view will do */
419                                                 ED_area_tag_redraw(sa);
420                                                 break;
421                                 }
422                         }
423                         break;
424                 case NC_NODE:
425                         if (wmn->action == NA_SELECTED) {
426                                 /* selection changed, so force refresh to flush (needs flag set to do syncing) */
427                                 saction->flag |= SACTION_TEMP_NEEDCHANSYNC;
428                                 ED_area_tag_refresh(sa);
429                         }
430                         break;
431                 case NC_SPACE:
432                         switch (wmn->data) {
433                                 case ND_SPACE_DOPESHEET:
434                                         ED_area_tag_redraw(sa);
435                                         break;
436                                 case ND_SPACE_CHANGED:
437                                         saction->flag |= SACTION_TEMP_NEEDCHANSYNC;
438                                         ED_area_tag_refresh(sa);
439                                         break;
440                         }
441                         break;
442                 case NC_WINDOW:
443                         if (saction->flag & SACTION_TEMP_NEEDCHANSYNC) {
444                                 /* force redraw/refresh after undo/redo - [#28962] */
445                                 ED_area_tag_refresh(sa);
446                         }
447                         break;
448         }
449 }
450
451 static void action_header_area_listener(ARegion *ar, wmNotifier *wmn)
452 {
453         /* context changes */
454         switch (wmn->category) {
455                 case NC_SCENE:
456                         switch (wmn->data) {
457                                 case ND_OB_ACTIVE:
458                                         ED_region_tag_redraw(ar);
459                                         break;
460                         }
461                         break;
462                 case NC_ID:
463                         if (wmn->action == NA_RENAME)
464                                 ED_region_tag_redraw(ar);
465                         break;
466         }
467 }
468
469 static void action_refresh(const bContext *C, ScrArea *sa)
470 {
471         SpaceAction *saction = (SpaceAction *)sa->spacedata.first;
472         
473         /* update the state of the animchannels in response to changes from the data they represent 
474          * NOTE: the temp flag is used to indicate when this needs to be done, and will be cleared once handled
475          */
476         if (saction->flag & SACTION_TEMP_NEEDCHANSYNC) {
477                 ARegion *ar;
478                 
479                 /* Perform syncing of channel state incl. selection
480                  * Active action setting also occurs here (as part of anim channel filtering in anim_filter.c)
481                  */
482                 ANIM_sync_animchannels_to_data(C);
483                 saction->flag &= ~SACTION_TEMP_NEEDCHANSYNC;
484                 
485                 /* Tag everything for redraw
486                  * - Regions (such as header) need to be manually tagged for redraw too
487                  *   or else they don't update [#28962]
488                  */
489                 ED_area_tag_redraw(sa);
490                 for (ar = sa->regionbase.first; ar; ar = ar->next)
491                         ED_region_tag_redraw(ar);
492         }
493         
494         /* region updates? */
495         // XXX re-sizing y-extents of tot should go here?
496 }
497
498 /* only called once, from space/spacetypes.c */
499 void ED_spacetype_action(void)
500 {
501         SpaceType *st = MEM_callocN(sizeof(SpaceType), "spacetype action");
502         ARegionType *art;
503         
504         st->spaceid = SPACE_ACTION;
505         strncpy(st->name, "Action", BKE_ST_MAXNAME);
506         
507         st->new = action_new;
508         st->free = action_free;
509         st->init = action_init;
510         st->duplicate = action_duplicate;
511         st->operatortypes = action_operatortypes;
512         st->keymap = action_keymap;
513         st->listener = action_listener;
514         st->refresh = action_refresh;
515         
516         /* regions: main window */
517         art = MEM_callocN(sizeof(ARegionType), "spacetype action region");
518         art->regionid = RGN_TYPE_WINDOW;
519         art->init = action_main_area_init;
520         art->draw = action_main_area_draw;
521         art->listener = action_main_area_listener;
522         art->keymapflag = ED_KEYMAP_VIEW2D | ED_KEYMAP_MARKERS | ED_KEYMAP_ANIMATION | ED_KEYMAP_FRAMES;
523
524         BLI_addhead(&st->regiontypes, art);
525         
526         /* regions: header */
527         art = MEM_callocN(sizeof(ARegionType), "spacetype action region");
528         art->regionid = RGN_TYPE_HEADER;
529         art->prefsizey = HEADERY;
530         art->keymapflag = ED_KEYMAP_UI | ED_KEYMAP_VIEW2D | ED_KEYMAP_FRAMES | ED_KEYMAP_HEADER;
531         
532         art->init = action_header_area_init;
533         art->draw = action_header_area_draw;
534         art->listener = action_header_area_listener;
535         
536         BLI_addhead(&st->regiontypes, art);
537         
538         /* regions: channels */
539         art = MEM_callocN(sizeof(ARegionType), "spacetype action region");
540         art->regionid = RGN_TYPE_CHANNELS;
541         art->prefsizex = 200;
542         art->keymapflag = ED_KEYMAP_UI | ED_KEYMAP_VIEW2D;
543         
544         art->init = action_channel_area_init;
545         art->draw = action_channel_area_draw;
546         art->listener = action_channel_area_listener;
547         
548         BLI_addhead(&st->regiontypes, art);
549         
550         
551         BKE_spacetype_register(st);
552 }
553