38919076b13a226be301ebf50e4d00632b4113be
[blender.git] / source / blender / editors / space_graph / space_graph.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_anim_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_global.h"
44 #include "BKE_main.h"
45 #include "BKE_fcurve.h"
46 #include "BKE_screen.h"
47
48 #include "ED_space_api.h"
49 #include "ED_screen.h"
50 #include "ED_anim_api.h"
51 #include "ED_markers.h"
52
53 #include "BIF_gl.h"
54
55 #include "WM_api.h"
56 #include "WM_types.h"
57
58 #include "UI_resources.h"
59 #include "UI_view2d.h"
60
61 #include "graph_intern.h"       // own include
62
63 /* ******************** manage regions ********************* */
64
65 ARegion *graph_has_buttons_region(ScrArea *sa)
66 {
67         ARegion *ar, *arnew;
68         
69         for (ar= sa->regionbase.first; ar; ar= ar->next) {
70                 if (ar->regiontype==RGN_TYPE_UI)
71                         return ar;
72         }
73         
74         /* add subdiv level; after main */
75         for (ar= sa->regionbase.first; ar; ar= ar->next) {
76                 if (ar->regiontype==RGN_TYPE_WINDOW)
77                         break;
78         }
79         
80         /* is error! */
81         if (ar==NULL) return NULL;
82         
83         arnew= MEM_callocN(sizeof(ARegion), "buttons for graph");
84         
85         BLI_insertlinkafter(&sa->regionbase, ar, arnew);
86         arnew->regiontype= RGN_TYPE_UI;
87         arnew->alignment= RGN_ALIGN_RIGHT;
88         
89         arnew->flag = RGN_FLAG_HIDDEN;
90         
91         return arnew;
92 }
93
94
95 /* ******************** default callbacks for ipo space ***************** */
96
97 static SpaceLink *graph_new(const bContext *C)
98 {
99         Scene *scene= CTX_data_scene(C);
100         ARegion *ar;
101         SpaceIpo *sipo;
102         
103         /* Graph Editor - general stuff */
104         sipo= MEM_callocN(sizeof(SpaceIpo), "init graphedit");
105         sipo->spacetype= SPACE_IPO;
106         
107         sipo->autosnap= SACTSNAP_FRAME;
108         
109         /* allocate DopeSheet data for Graph Editor */
110         sipo->ads= MEM_callocN(sizeof(bDopeSheet), "GraphEdit DopeSheet");
111         sipo->ads->source= (ID *)scene;
112         
113         /* header */
114         ar= MEM_callocN(sizeof(ARegion), "header for graphedit");
115         
116         BLI_addtail(&sipo->regionbase, ar);
117         ar->regiontype= RGN_TYPE_HEADER;
118         ar->alignment= RGN_ALIGN_BOTTOM;
119         
120         /* channels */
121         ar= MEM_callocN(sizeof(ARegion), "channels area for graphedit");
122         
123         BLI_addtail(&sipo->regionbase, ar);
124         ar->regiontype= RGN_TYPE_CHANNELS;
125         ar->alignment= RGN_ALIGN_LEFT;
126         
127         ar->v2d.scroll = (V2D_SCROLL_RIGHT|V2D_SCROLL_BOTTOM);
128         
129         /* ui buttons */
130         ar= MEM_callocN(sizeof(ARegion), "buttons area for graphedit");
131         
132         BLI_addtail(&sipo->regionbase, ar);
133         ar->regiontype= RGN_TYPE_UI;
134         ar->alignment= RGN_ALIGN_RIGHT;
135         ar->flag = RGN_FLAG_HIDDEN;
136         
137         /* main area */
138         ar= MEM_callocN(sizeof(ARegion), "main area for graphedit");
139         
140         BLI_addtail(&sipo->regionbase, ar);
141         ar->regiontype= RGN_TYPE_WINDOW;
142         
143         ar->v2d.tot.xmin= 0.0f;
144         ar->v2d.tot.ymin= (float)scene->r.sfra - 10.0f;
145         ar->v2d.tot.xmax= (float)scene->r.efra;
146         ar->v2d.tot.ymax= 10.0f;
147         
148         ar->v2d.cur= ar->v2d.tot;
149         
150         ar->v2d.min[0]= FLT_MIN;
151         ar->v2d.min[1]= FLT_MIN;
152
153         ar->v2d.max[0]= MAXFRAMEF;
154         ar->v2d.max[1]= FLT_MAX;
155         
156         ar->v2d.scroll= (V2D_SCROLL_BOTTOM|V2D_SCROLL_SCALE_HORIZONTAL);
157         ar->v2d.scroll |= (V2D_SCROLL_LEFT|V2D_SCROLL_SCALE_VERTICAL);
158         
159         ar->v2d.keeptot= 0;
160         
161         return (SpaceLink *)sipo;
162 }
163
164 /* not spacelink itself */
165 static void graph_free(SpaceLink *sl)
166 {       
167         SpaceIpo *si= (SpaceIpo *)sl;
168         
169         if (si->ads) {
170                 BLI_freelistN(&si->ads->chanbase);
171                 MEM_freeN(si->ads);
172         }
173         
174         if (si->ghostCurves.first)
175                 free_fcurves(&si->ghostCurves);
176 }
177
178
179 /* spacetype; init callback */
180 static void graph_init(struct wmWindowManager *UNUSED(wm), ScrArea *sa)
181 {
182         SpaceIpo *sipo= (SpaceIpo *)sa->spacedata.first;
183         
184         /* init dopesheet data if non-existant (i.e. for old files) */
185         if (sipo->ads == NULL) {
186                 sipo->ads= MEM_callocN(sizeof(bDopeSheet), "GraphEdit DopeSheet");
187                 sipo->ads->source= (ID *)(G.main->scene.first); // FIXME: this is a really nasty hack here for now...
188         }
189
190         ED_area_tag_refresh(sa);
191 }
192
193 static SpaceLink *graph_duplicate(SpaceLink *sl)
194 {
195         SpaceIpo *sipon= MEM_dupallocN(sl);
196         
197         /* clear or remove stuff from old */
198         BLI_duplicatelist(&sipon->ghostCurves, &((SpaceIpo *)sl)->ghostCurves);
199         sipon->ads= MEM_dupallocN(sipon->ads);
200         
201         return (SpaceLink *)sipon;
202 }
203
204 /* add handlers, stuff you only do once or on area/region changes */
205 static void graph_main_area_init(wmWindowManager *wm, ARegion *ar)
206 {
207         wmKeyMap *keymap;
208         
209         UI_view2d_region_reinit(&ar->v2d, V2D_COMMONVIEW_CUSTOM, ar->winx, ar->winy);
210         
211         /* own keymap */
212         keymap= WM_keymap_find(wm->defaultconf, "Graph Editor", SPACE_IPO, 0);
213         WM_event_add_keymap_handler_bb(&ar->handlers, keymap, &ar->v2d.mask, &ar->winrct);
214         keymap= WM_keymap_find(wm->defaultconf, "Graph Editor Generic", SPACE_IPO, 0);
215         WM_event_add_keymap_handler(&ar->handlers, keymap);
216 }
217
218 static void graph_main_area_draw(const bContext *C, ARegion *ar)
219 {
220         /* draw entirely, view changes should be handled here */
221         SpaceIpo *sipo= CTX_wm_space_graph(C);
222         bAnimContext ac;
223         View2D *v2d= &ar->v2d;
224         View2DGrid *grid;
225         View2DScrollers *scrollers;
226         float col[3];
227         short unitx=0, unity=V2D_UNIT_VALUES, flag=0;
228         
229         /* clear and setup matrix */
230         UI_GetThemeColor3fv(TH_BACK, col);
231         glClearColor(col[0], col[1], col[2], 0.0);
232         glClear(GL_COLOR_BUFFER_BIT);
233         
234         UI_view2d_view_ortho(v2d);
235         
236         /* grid */
237         unitx= (sipo->flag & SIPO_DRAWTIME)? V2D_UNIT_SECONDS : V2D_UNIT_FRAMESCALE;
238         grid= UI_view2d_grid_calc(CTX_data_scene(C), v2d, unitx, V2D_GRID_NOCLAMP, unity, V2D_GRID_NOCLAMP, ar->winx, ar->winy);
239         UI_view2d_grid_draw(v2d, grid, V2D_GRIDLINES_ALL);
240         
241         /* draw data */
242         if (ANIM_animdata_get_context(C, &ac)) {
243                 /* draw ghost curves */
244                 graph_draw_ghost_curves(&ac, sipo, ar);
245                 
246                 /* draw curves twice - unselected, then selected, so that the are fewer occlusion problems */
247                 graph_draw_curves(&ac, sipo, ar, grid, 0);
248                 graph_draw_curves(&ac, sipo, ar, grid, 1);
249                 
250                 /* XXX the slow way to set tot rect... but for nice sliders needed (ton) */
251                 get_graph_keyframe_extents(&ac, &v2d->tot.xmin, &v2d->tot.xmax, &v2d->tot.ymin, &v2d->tot.ymax);
252                 /* extra offset so that these items are visible */
253                 v2d->tot.xmin -= 10.0f;
254                 v2d->tot.xmax += 10.0f;
255         }
256         
257         /* only free grid after drawing data, as we need to use it to determine sampling rate */
258         UI_view2d_grid_free(grid);
259         
260         /* horizontal component of value-cursor (value line before the current frame line) */
261         if ((sipo->flag & SIPO_NODRAWCURSOR)==0) {
262                 float vec[2];
263                 
264                 /* Draw a green line to indicate the cursor value */
265                 vec[1]= sipo->cursorVal;
266                 
267                 UI_ThemeColorShadeAlpha(TH_CFRAME, -10, -50);
268                 glLineWidth(2.0);
269                 
270                 glEnable(GL_BLEND);
271                 glBegin(GL_LINE_STRIP);
272                         vec[0]= v2d->cur.xmin;
273                         glVertex2fv(vec);
274                         
275                         vec[0]= v2d->cur.xmax;
276                         glVertex2fv(vec);
277                 glEnd(); // GL_LINE_STRIP
278                 glDisable(GL_BLEND);
279         }
280         
281         /* current frame */
282         if (sipo->flag & SIPO_DRAWTIME)         flag |= DRAWCFRA_UNIT_SECONDS;
283         if ((sipo->flag & SIPO_NODRAWCFRANUM)==0)  flag |= DRAWCFRA_SHOW_NUMBOX;
284         ANIM_draw_cfra(C, v2d, flag);
285         
286         /* markers */
287         UI_view2d_view_orthoSpecial(ar, v2d, 1);
288         draw_markers_time(C, 0);
289         
290         /* preview range */
291         UI_view2d_view_ortho(v2d);
292         ANIM_draw_previewrange(C, v2d);
293         
294         /* reset view matrix */
295         UI_view2d_view_restore(C);
296         
297         /* scrollers */
298                 // FIXME: args for scrollers depend on the type of data being shown...
299         scrollers= UI_view2d_scrollers_calc(C, v2d, unitx, V2D_GRID_NOCLAMP, unity, V2D_GRID_NOCLAMP);
300         UI_view2d_scrollers_draw(C, v2d, scrollers);
301         UI_view2d_scrollers_free(scrollers);
302 }
303
304 static void graph_channel_area_init(wmWindowManager *wm, ARegion *ar)
305 {
306         wmKeyMap *keymap;
307         
308         UI_view2d_region_reinit(&ar->v2d, V2D_COMMONVIEW_LIST, ar->winx, ar->winy);
309         
310         /* own keymap */
311         keymap= WM_keymap_find(wm->defaultconf, "Animation Channels", 0, 0);
312         WM_event_add_keymap_handler_bb(&ar->handlers, keymap, &ar->v2d.mask, &ar->winrct);
313         keymap= WM_keymap_find(wm->defaultconf, "Graph Editor Generic", SPACE_IPO, 0);
314         WM_event_add_keymap_handler(&ar->handlers, keymap);
315 }
316
317 static void graph_channel_area_draw(const bContext *C, ARegion *ar)
318 {
319         bAnimContext ac;
320         View2D *v2d= &ar->v2d;
321         View2DScrollers *scrollers;
322         float col[3];
323         
324         /* clear and setup matrix */
325         UI_GetThemeColor3fv(TH_BACK, col);
326         glClearColor(col[0], col[1], col[2], 0.0);
327         glClear(GL_COLOR_BUFFER_BIT);
328         
329         UI_view2d_view_ortho(v2d);
330         
331         /* draw channels */
332         if (ANIM_animdata_get_context(C, &ac)) {
333                 graph_draw_channel_names((bContext*)C, &ac, ar);
334         }
335         
336         /* reset view matrix */
337         UI_view2d_view_restore(C);
338         
339         /* scrollers */
340         scrollers= UI_view2d_scrollers_calc(C, v2d, V2D_ARG_DUMMY, V2D_ARG_DUMMY, V2D_ARG_DUMMY, V2D_ARG_DUMMY);
341         UI_view2d_scrollers_draw(C, v2d, scrollers);
342         UI_view2d_scrollers_free(scrollers);
343 }
344
345 /* add handlers, stuff you only do once or on area/region changes */
346 static void graph_header_area_init(wmWindowManager *UNUSED(wm), ARegion *ar)
347 {
348         ED_region_header_init(ar);
349 }
350
351 static void graph_header_area_draw(const bContext *C, ARegion *ar)
352 {
353         ED_region_header(C, ar);
354 }
355
356 /* add handlers, stuff you only do once or on area/region changes */
357 static void graph_buttons_area_init(wmWindowManager *wm, ARegion *ar)
358 {
359         wmKeyMap *keymap;
360         
361         ED_region_panels_init(wm, ar);
362
363         keymap= WM_keymap_find(wm->defaultconf, "Graph Editor Generic", SPACE_IPO, 0);
364         WM_event_add_keymap_handler_bb(&ar->handlers, keymap, &ar->v2d.mask, &ar->winrct);
365 }
366
367 static void graph_buttons_area_draw(const bContext *C, ARegion *ar)
368 {
369         ED_region_panels(C, ar, 1, NULL, -1);
370 }
371
372 static void graph_region_listener(ARegion *ar, wmNotifier *wmn)
373 {
374         /* context changes */
375         switch(wmn->category) {
376                 case NC_ANIMATION:
377                         ED_region_tag_redraw(ar);
378                         break;
379                 case NC_SCENE:
380                         switch(wmn->data) {
381                                 case ND_RENDER_OPTIONS:
382                                 case ND_OB_ACTIVE:
383                                 case ND_FRAME:
384                                 case ND_MARKERS:
385                                         ED_region_tag_redraw(ar);
386                                         break;
387                                 case ND_SEQUENCER:
388                                         if (wmn->action == NA_SELECTED)
389                                                 ED_region_tag_redraw(ar);
390                                         break;
391                         }
392                         break;
393                 case NC_OBJECT:
394                         switch(wmn->data) {
395                                 case ND_BONE_ACTIVE:
396                                 case ND_BONE_SELECT:
397                                 case ND_KEYS:
398                                         ED_region_tag_redraw(ar);
399                                         break;
400                                 case ND_MODIFIER:
401                                         if(wmn->action == NA_RENAME)
402                                                 ED_region_tag_redraw(ar);
403                                         break;
404                         }
405                         break;
406                 case NC_NODE:
407                         switch(wmn->action) {
408                                 case NA_EDITED:
409                                 case NA_SELECTED:
410                                         ED_region_tag_redraw(ar);
411                                         break;
412                         }
413                         break;
414                 case NC_ID:
415                         if(wmn->action == NA_RENAME)
416                                 ED_region_tag_redraw(ar);
417                         break;
418                 default:
419                         if(wmn->data==ND_KEYS)
420                                 ED_region_tag_redraw(ar);
421                                 
422         }
423 }
424
425 /* editor level listener */
426 static void graph_listener(ScrArea *sa, wmNotifier *wmn)
427 {
428         SpaceIpo *sipo= (SpaceIpo *)sa->spacedata.first;
429         
430         /* context changes */
431         switch (wmn->category) {
432                 case NC_ANIMATION:
433                         /* for selection changes of animation data, we can just redraw... otherwise autocolor might need to be done again */
434                         if (ELEM(wmn->data, ND_KEYFRAME, ND_ANIMCHAN) && (wmn->action == NA_SELECTED))
435                                 ED_area_tag_redraw(sa);
436                         else
437                                 ED_area_tag_refresh(sa);
438                         break;
439                 case NC_SCENE:
440                         switch (wmn->data) {    
441                                 case ND_OB_ACTIVE:      /* selection changed, so force refresh to flush (needs flag set to do syncing)  */
442                                 case ND_OB_SELECT:
443                                         sipo->flag |= SIPO_TEMP_NEEDCHANSYNC;
444                                         ED_area_tag_refresh(sa);
445                                         break;
446                                         
447                                 default: /* just redrawing the view will do */
448                                         ED_area_tag_redraw(sa);
449                                         break;
450                         }
451                         break;
452                 case NC_OBJECT:
453                         switch (wmn->data) {
454                                 case ND_BONE_SELECT:    /* selection changed, so force refresh to flush (needs flag set to do syncing) */
455                                 case ND_BONE_ACTIVE:
456                                         sipo->flag |= SIPO_TEMP_NEEDCHANSYNC;
457                                         ED_area_tag_refresh(sa);
458                                         break;
459                                 case ND_TRANSFORM:
460                                         break; /*do nothing*/                                   
461                                         
462                                 default: /* just redrawing the view will do */
463                                         ED_area_tag_redraw(sa);
464                                         break;
465                         }
466                         break;
467                 case NC_SPACE:
468                         if(wmn->data == ND_SPACE_GRAPH)
469                                 ED_area_tag_redraw(sa);
470                         break;
471                 
472                 // XXX: restore the case below if not enough updates occur...
473                 //default:
474                 //      if(wmn->data==ND_KEYS)
475                 //              ED_area_tag_redraw(sa);
476         }
477 }
478
479
480
481 static void graph_refresh(const bContext *C, ScrArea *sa)
482 {
483         SpaceIpo *sipo = (SpaceIpo *)sa->spacedata.first;
484         bAnimContext ac;
485         
486         /* updates to data needed depends on Graph Editor mode... */
487         switch (sipo->mode) {
488                 case SIPO_MODE_ANIMATION: /* all animation */
489                 {
490                         
491                 }
492                         break;
493                 
494                 case SIPO_MODE_DRIVERS: /* drivers only  */
495                 {
496                 
497                 }
498                         break; 
499         }
500         
501         /* region updates? */
502         // XXX resizing y-extents of tot should go here?
503         
504         /* update the state of the animchannels in response to changes from the data they represent 
505          * NOTE: the temp flag is used to indicate when this needs to be done, and will be cleared once handled
506          */
507         if (sipo->flag & SIPO_TEMP_NEEDCHANSYNC) {
508                 ANIM_sync_animchannels_to_data(C);
509                 sipo->flag &= ~SIPO_TEMP_NEEDCHANSYNC;
510         }
511         
512         /* init/adjust F-Curve colors */
513         if (ANIM_animdata_get_context(C, &ac)) {
514                 ListBase anim_data = {NULL, NULL};
515                 bAnimListElem *ale;
516                 int filter;
517                 int items, i;
518                 
519                 /* build list of F-Curves which will be visible as channels in channel-region
520                  *      - we don't include ANIMFILTER_CURVEVISIBLE filter, as that will result in a 
521                  *        mismatch between channel-colors and the drawn curves
522                  */
523                 filter= (ANIMFILTER_VISIBLE|ANIMFILTER_CURVESONLY|ANIMFILTER_NODUPLIS);
524                 items= ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
525                 
526                 /* loop over F-Curves, assigning colors */
527                 for (ale=anim_data.first, i=0; ale; ale= ale->next, i++) {
528                         FCurve *fcu= (FCurve *)ale->data;
529                         
530                         /* set color of curve here */
531                         switch (fcu->color_mode) {
532                                 case FCURVE_COLOR_CUSTOM:
533                                         /* User has defined a custom color for this curve already (we assume it's not going to cause clashes with text colors),
534                                          * which should be left alone... Nothing needs to be done here.
535                                          */
536                                         break;
537                                         
538                                 case FCURVE_COLOR_AUTO_RGB:
539                                 {
540                                         /* F-Curve's array index is automatically mapped to RGB values. This works best of 3-value vectors. 
541                                          * TODO: find a way to module the hue so that not all curves have same color...
542                                          */
543                                         float *col= fcu->color;
544
545                                         switch(fcu->array_index) {
546                                         case 0:
547                                                 col[0]= 1.0f; col[1]= 0.0f; col[2]= 0.0f;
548                                                 break;
549                                         case 1:
550                                                 col[0]= 0.0f; col[1]= 1.0f; col[2]= 0.0f;
551                                                 break;
552                                         case 2:
553                                                 col[0]= 0.0f; col[1]= 0.0f; col[2]= 1.0f;
554                                                 break;
555                                         default:
556                                                 /* 'unknown' color - bluish so as to not conflict with handles */
557                                                 col[0]= 0.3f; col[1]= 0.8f; col[2]= 1.0f;
558                                                 break;
559                                         }
560                                 }
561                                         break;
562                                 
563                                 case FCURVE_COLOR_AUTO_RAINBOW:
564                                 default:
565                                 {
566                                         /* determine color 'automatically' using 'magic function' which uses the given args
567                                          * of current item index + total items to determine some RGB color
568                                          */
569                                         getcolor_fcurve_rainbow(i, items, fcu->color);
570                                 }
571                                         break;
572                         }
573                 }
574                 
575                 /* free temp list */
576                 BLI_freelistN(&anim_data);
577         }
578 }
579
580 /* only called once, from space/spacetypes.c */
581 void ED_spacetype_ipo(void)
582 {
583         SpaceType *st= MEM_callocN(sizeof(SpaceType), "spacetype ipo");
584         ARegionType *art;
585         
586         st->spaceid= SPACE_IPO;
587         strncpy(st->name, "Graph", BKE_ST_MAXNAME);
588         
589         st->new= graph_new;
590         st->free= graph_free;
591         st->init= graph_init;
592         st->duplicate= graph_duplicate;
593         st->operatortypes= graphedit_operatortypes;
594         st->keymap= graphedit_keymap;
595         st->listener= graph_listener;
596         st->refresh= graph_refresh;
597         
598         /* regions: main window */
599         art= MEM_callocN(sizeof(ARegionType), "spacetype graphedit region");
600         art->regionid = RGN_TYPE_WINDOW;
601         art->init= graph_main_area_init;
602         art->draw= graph_main_area_draw;
603         art->listener= graph_region_listener;
604         art->keymapflag= ED_KEYMAP_VIEW2D|ED_KEYMAP_MARKERS|ED_KEYMAP_ANIMATION|ED_KEYMAP_FRAMES;
605
606         BLI_addhead(&st->regiontypes, art);
607         
608         /* regions: header */
609         art= MEM_callocN(sizeof(ARegionType), "spacetype graphedit region");
610         art->regionid = RGN_TYPE_HEADER;
611         art->prefsizey= HEADERY;
612         art->keymapflag= ED_KEYMAP_UI|ED_KEYMAP_VIEW2D|ED_KEYMAP_FRAMES|ED_KEYMAP_HEADER;
613         art->listener= graph_region_listener;
614         art->init= graph_header_area_init;
615         art->draw= graph_header_area_draw;
616         
617         BLI_addhead(&st->regiontypes, art);
618         
619         /* regions: channels */
620         art= MEM_callocN(sizeof(ARegionType), "spacetype graphedit region");
621         art->regionid = RGN_TYPE_CHANNELS;
622         art->prefsizex= 200+V2D_SCROLL_WIDTH; /* 200 is the 'standard', but due to scrollers, we want a bit more to fit the lock icons in */
623         art->keymapflag= ED_KEYMAP_UI|ED_KEYMAP_VIEW2D|ED_KEYMAP_FRAMES;
624         art->listener= graph_region_listener;
625         art->init= graph_channel_area_init;
626         art->draw= graph_channel_area_draw;
627         
628         BLI_addhead(&st->regiontypes, art);
629         
630         /* regions: UI buttons */
631         art= MEM_callocN(sizeof(ARegionType), "spacetype graphedit region");
632         art->regionid = RGN_TYPE_UI;
633         art->prefsizex= 200;
634         art->keymapflag= ED_KEYMAP_UI;
635         art->listener= graph_region_listener;
636         art->init= graph_buttons_area_init;
637         art->draw= graph_buttons_area_draw;
638         
639         BLI_addhead(&st->regiontypes, art);
640
641         graph_buttons_register(art);
642         
643         BKE_spacetype_register(st);
644 }
645