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