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