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