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