Fix #34941: Space.draw_handler_add now supports PRE_VIEW and POST_VIEW callbacks
[blender-staging.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         /* force immediate init of any invalid F-Curve colors */
193         sipo->flag |= SIPO_TEMP_NEEDCHANSYNC;
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         ED_region_draw_cb_draw(C, ar, REGION_DRAW_PRE_VIEW);
246
247         /* draw data */
248         if (ANIM_animdata_get_context(C, &ac)) {
249                 /* draw ghost curves */
250                 graph_draw_ghost_curves(&ac, sipo, ar);
251                 
252                 /* draw curves twice - unselected, then selected, so that the are fewer occlusion problems */
253                 graph_draw_curves(&ac, sipo, ar, grid, 0);
254                 graph_draw_curves(&ac, sipo, ar, grid, 1);
255                 
256                 /* XXX the slow way to set tot rect... but for nice sliders needed (ton) */
257                 get_graph_keyframe_extents(&ac, &v2d->tot.xmin, &v2d->tot.xmax, &v2d->tot.ymin, &v2d->tot.ymax, FALSE, TRUE);
258                 /* extra offset so that these items are visible */
259                 v2d->tot.xmin -= 10.0f;
260                 v2d->tot.xmax += 10.0f;
261         }
262         
263         /* only free grid after drawing data, as we need to use it to determine sampling rate */
264         UI_view2d_grid_free(grid);
265         
266         /* horizontal component of value-cursor (value line before the current frame line) */
267         if ((sipo->flag & SIPO_NODRAWCURSOR) == 0) {
268                 float vec[2];
269                 
270                 /* Draw a green line to indicate the cursor value */
271                 vec[1] = sipo->cursorVal;
272                 
273                 UI_ThemeColorShadeAlpha(TH_CFRAME, -10, -50);
274                 glLineWidth(2.0);
275                 
276                 glEnable(GL_BLEND);
277                 glBegin(GL_LINE_STRIP);
278                 vec[0] = v2d->cur.xmin;
279                 glVertex2fv(vec);
280                         
281                 vec[0] = v2d->cur.xmax;
282                 glVertex2fv(vec);
283                 glEnd(); // GL_LINE_STRIP
284                 glDisable(GL_BLEND);
285         }
286         
287         /* current frame */
288         if (sipo->flag & SIPO_DRAWTIME) flag |= DRAWCFRA_UNIT_SECONDS;
289         if ((sipo->flag & SIPO_NODRAWCFRANUM) == 0) flag |= DRAWCFRA_SHOW_NUMBOX;
290         ANIM_draw_cfra(C, v2d, flag);
291         
292         /* markers */
293         UI_view2d_view_orthoSpecial(ar, v2d, 1);
294         draw_markers_time(C, 0);
295         
296         /* preview range */
297         UI_view2d_view_ortho(v2d);
298         ANIM_draw_previewrange(C, v2d);
299         
300         /* callback */
301         UI_view2d_view_ortho(v2d);
302         ED_region_draw_cb_draw(C, ar, REGION_DRAW_POST_VIEW);
303
304         /* reset view matrix */
305         UI_view2d_view_restore(C);
306         
307         /* scrollers */
308         // FIXME: args for scrollers depend on the type of data being shown...
309         scrollers = UI_view2d_scrollers_calc(C, v2d, unitx, V2D_GRID_NOCLAMP, unity, V2D_GRID_NOCLAMP);
310         UI_view2d_scrollers_draw(C, v2d, scrollers);
311         UI_view2d_scrollers_free(scrollers);
312 }
313
314 static void graph_channel_area_init(wmWindowManager *wm, ARegion *ar)
315 {
316         wmKeyMap *keymap;
317         
318         /* make sure we keep the hide flags */
319         ar->v2d.scroll |= (V2D_SCROLL_RIGHT | V2D_SCROLL_BOTTOM);
320         ar->v2d.scroll &= ~(V2D_SCROLL_LEFT | V2D_SCROLL_TOP);  /* prevent any noise of past */
321         ar->v2d.scroll |= V2D_SCROLL_HORIZONTAL_HIDE;
322         ar->v2d.scroll |= V2D_SCROLL_VERTICAL_HIDE;
323         
324         UI_view2d_region_reinit(&ar->v2d, V2D_COMMONVIEW_LIST, ar->winx, ar->winy);
325         
326         /* own keymap */
327         keymap = WM_keymap_find(wm->defaultconf, "Animation Channels", 0, 0);
328         WM_event_add_keymap_handler_bb(&ar->handlers, keymap, &ar->v2d.mask, &ar->winrct);
329         keymap = WM_keymap_find(wm->defaultconf, "Graph Editor Generic", SPACE_IPO, 0);
330         WM_event_add_keymap_handler(&ar->handlers, keymap);
331 }
332
333 static void graph_channel_area_draw(const bContext *C, ARegion *ar)
334 {
335         bAnimContext ac;
336         View2D *v2d = &ar->v2d;
337         View2DScrollers *scrollers;
338         float col[3];
339         
340         /* clear and setup matrix */
341         UI_GetThemeColor3fv(TH_BACK, col);
342         glClearColor(col[0], col[1], col[2], 0.0);
343         glClear(GL_COLOR_BUFFER_BIT);
344         
345         UI_view2d_view_ortho(v2d);
346         
347         /* draw channels */
348         if (ANIM_animdata_get_context(C, &ac)) {
349                 graph_draw_channel_names((bContext *)C, &ac, ar);
350         }
351         
352         /* reset view matrix */
353         UI_view2d_view_restore(C);
354         
355         /* scrollers */
356         scrollers = UI_view2d_scrollers_calc(C, v2d, V2D_ARG_DUMMY, V2D_ARG_DUMMY, V2D_ARG_DUMMY, V2D_ARG_DUMMY);
357         UI_view2d_scrollers_draw(C, v2d, scrollers);
358         UI_view2d_scrollers_free(scrollers);
359 }
360
361 /* add handlers, stuff you only do once or on area/region changes */
362 static void graph_header_area_init(wmWindowManager *UNUSED(wm), ARegion *ar)
363 {
364         ED_region_header_init(ar);
365 }
366
367 static void graph_header_area_draw(const bContext *C, ARegion *ar)
368 {
369         ED_region_header(C, ar);
370 }
371
372 /* add handlers, stuff you only do once or on area/region changes */
373 static void graph_buttons_area_init(wmWindowManager *wm, ARegion *ar)
374 {
375         wmKeyMap *keymap;
376         
377         ED_region_panels_init(wm, ar);
378
379         keymap = WM_keymap_find(wm->defaultconf, "Graph Editor Generic", SPACE_IPO, 0);
380         WM_event_add_keymap_handler_bb(&ar->handlers, keymap, &ar->v2d.mask, &ar->winrct);
381 }
382
383 static void graph_buttons_area_draw(const bContext *C, ARegion *ar)
384 {
385         ED_region_panels(C, ar, 1, NULL, -1);
386 }
387
388 static void graph_region_listener(ARegion *ar, wmNotifier *wmn)
389 {
390         /* context changes */
391         switch (wmn->category) {
392                 case NC_ANIMATION:
393                         ED_region_tag_redraw(ar);
394                         break;
395                 case NC_SCENE:
396                         switch (wmn->data) {
397                                 case ND_RENDER_OPTIONS:
398                                 case ND_OB_ACTIVE:
399                                 case ND_FRAME:
400                                 case ND_MARKERS:
401                                         ED_region_tag_redraw(ar);
402                                         break;
403                                 case ND_SEQUENCER:
404                                         if (wmn->action == NA_SELECTED)
405                                                 ED_region_tag_redraw(ar);
406                                         break;
407                         }
408                         break;
409                 case NC_OBJECT:
410                         switch (wmn->data) {
411                                 case ND_BONE_ACTIVE:
412                                 case ND_BONE_SELECT:
413                                 case ND_KEYS:
414                                         ED_region_tag_redraw(ar);
415                                         break;
416                                 case ND_MODIFIER:
417                                         if (wmn->action == NA_RENAME)
418                                                 ED_region_tag_redraw(ar);
419                                         break;
420                         }
421                         break;
422                 case NC_NODE:
423                         switch (wmn->action) {
424                                 case NA_EDITED:
425                                 case NA_SELECTED:
426                                         ED_region_tag_redraw(ar);
427                                         break;
428                         }
429                         break;
430                 case NC_ID:
431                         if (wmn->action == NA_RENAME)
432                                 ED_region_tag_redraw(ar);
433                         break;
434                 default:
435                         if (wmn->data == ND_KEYS)
436                                 ED_region_tag_redraw(ar);
437                                 
438         }
439 }
440
441 /* editor level listener */
442 static void graph_listener(ScrArea *sa, wmNotifier *wmn)
443 {
444         SpaceIpo *sipo = (SpaceIpo *)sa->spacedata.first;
445         
446         /* context changes */
447         switch (wmn->category) {
448                 case NC_ANIMATION:
449                         /* for selection changes of animation data, we can just redraw... otherwise autocolor might need to be done again */
450                         if (ELEM(wmn->data, ND_KEYFRAME, ND_ANIMCHAN) && (wmn->action == NA_SELECTED))
451                                 ED_area_tag_redraw(sa);
452                         else
453                                 ED_area_tag_refresh(sa);
454                         break;
455                 case NC_SCENE:
456                         switch (wmn->data) {
457                                 case ND_OB_ACTIVE:  /* selection changed, so force refresh to flush (needs flag set to do syncing)  */
458                                 case ND_OB_SELECT:
459                                         sipo->flag |= SIPO_TEMP_NEEDCHANSYNC;
460                                         ED_area_tag_refresh(sa);
461                                         break;
462                                         
463                                 default: /* just redrawing the view will do */
464                                         ED_area_tag_redraw(sa);
465                                         break;
466                         }
467                         break;
468                 case NC_OBJECT:
469                         switch (wmn->data) {
470                                 case ND_BONE_SELECT:    /* selection changed, so force refresh to flush (needs flag set to do syncing) */
471                                 case ND_BONE_ACTIVE:
472                                         sipo->flag |= SIPO_TEMP_NEEDCHANSYNC;
473                                         ED_area_tag_refresh(sa);
474                                         break;
475                                 case ND_TRANSFORM:
476                                         break; /*do nothing*/
477                                         
478                                 default: /* just redrawing the view will do */
479                                         ED_area_tag_redraw(sa);
480                                         break;
481                         }
482                         break;
483                 case NC_NODE:
484                         if (wmn->action == NA_SELECTED) {
485                                 /* selection changed, so force refresh to flush (needs flag set to do syncing) */
486                                 sipo->flag |= SIPO_TEMP_NEEDCHANSYNC;
487                                 ED_area_tag_refresh(sa);
488                         }
489                         break;
490                 case NC_SPACE:
491                         if (wmn->data == ND_SPACE_GRAPH)
492                                 ED_area_tag_redraw(sa);
493                         break;
494                 case NC_WINDOW:
495                         if (sipo->flag & SIPO_TEMP_NEEDCHANSYNC) {
496                                 /* force redraw/refresh after undo/redo - prevents "black curve" problem */
497                                 ED_area_tag_refresh(sa);
498                         }
499                         break;
500                         
501                         // XXX: restore the case below if not enough updates occur...
502                         //default:
503                         //      if (wmn->data == ND_KEYS)
504                         //              ED_area_tag_redraw(sa);
505         }
506 }
507
508
509
510 static void graph_refresh(const bContext *C, ScrArea *sa)
511 {
512         SpaceIpo *sipo = (SpaceIpo *)sa->spacedata.first;
513         bAnimContext ac;
514         
515         /* updates to data needed depends on Graph Editor mode... */
516         switch (sipo->mode) {
517                 case SIPO_MODE_ANIMATION: /* all animation */
518                 {
519                         
520                 }
521                 break;
522                 
523                 case SIPO_MODE_DRIVERS: /* drivers only  */
524                 {
525                 
526                 }
527                 break;
528         }
529         
530         /* region updates? */
531         // XXX re-sizing y-extents of tot should go here?
532         
533         /* update the state of the animchannels in response to changes from the data they represent 
534          * NOTE: the temp flag is used to indicate when this needs to be done, and will be cleared once handled
535          */
536         if (sipo->flag & SIPO_TEMP_NEEDCHANSYNC) {
537                 ANIM_sync_animchannels_to_data(C);
538                 sipo->flag &= ~SIPO_TEMP_NEEDCHANSYNC;
539                 ED_area_tag_redraw(sa);
540         }
541         
542         /* init/adjust F-Curve colors */
543         if (ANIM_animdata_get_context(C, &ac)) {
544                 ListBase anim_data = {NULL, NULL};
545                 bAnimListElem *ale;
546                 size_t items;
547                 int filter;
548                 int i;
549                 
550                 /* build list of F-Curves which will be visible as channels in channel-region
551                  *  - we don't include ANIMFILTER_CURVEVISIBLE filter, as that will result in a
552                  *    mismatch between channel-colors and the drawn curves
553                  */
554                 filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_NODUPLIS);
555                 items = ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
556                 
557                 /* loop over F-Curves, assigning colors */
558                 for (ale = anim_data.first, i = 0; ale; ale = ale->next, i++) {
559                         FCurve *fcu = (FCurve *)ale->data;
560                         
561                         /* set color of curve here */
562                         switch (fcu->color_mode) {
563                                 case FCURVE_COLOR_CUSTOM:
564                                         /* User has defined a custom color for this curve already (we assume it's not going to cause clashes with text colors),
565                                          * which should be left alone... Nothing needs to be done here.
566                                          */
567                                         break;
568                                         
569                                 case FCURVE_COLOR_AUTO_RGB:
570                                 {
571                                         /* F-Curve's array index is automatically mapped to RGB values. This works best of 3-value vectors. 
572                                          * TODO: find a way to module the hue so that not all curves have same color...
573                                          */
574                                         float *col = fcu->color;
575                                         
576                                         switch (fcu->array_index) {
577                                                 case 0:
578                                                         UI_GetThemeColor3fv(TH_AXIS_X, col);
579                                                         break;
580                                                 case 1:
581                                                         UI_GetThemeColor3fv(TH_AXIS_Y, col);
582                                                         break;
583                                                 case 2:
584                                                         UI_GetThemeColor3fv(TH_AXIS_Z, col);
585                                                         break;
586                                                 default:
587                                                         /* 'unknown' color - bluish so as to not conflict with handles */
588                                                         col[0] = 0.3f; col[1] = 0.8f; col[2] = 1.0f;
589                                                         break;
590                                         }
591                                 }
592                                 break;
593                                 
594                                 case FCURVE_COLOR_AUTO_RAINBOW:
595                                 default:
596                                 {
597                                         /* determine color 'automatically' using 'magic function' which uses the given args
598                                          * of current item index + total items to determine some RGB color
599                                          */
600                                         getcolor_fcurve_rainbow(i, items, fcu->color);
601                                 }
602                                 break;
603                         }
604                 }
605                 
606                 /* free temp list */
607                 BLI_freelistN(&anim_data);
608         }
609 }
610
611 /* only called once, from space/spacetypes.c */
612 void ED_spacetype_ipo(void)
613 {
614         SpaceType *st = MEM_callocN(sizeof(SpaceType), "spacetype ipo");
615         ARegionType *art;
616         
617         st->spaceid = SPACE_IPO;
618         strncpy(st->name, "Graph", BKE_ST_MAXNAME);
619         
620         st->new = graph_new;
621         st->free = graph_free;
622         st->init = graph_init;
623         st->duplicate = graph_duplicate;
624         st->operatortypes = graphedit_operatortypes;
625         st->keymap = graphedit_keymap;
626         st->listener = graph_listener;
627         st->refresh = graph_refresh;
628         
629         /* regions: main window */
630         art = MEM_callocN(sizeof(ARegionType), "spacetype graphedit region");
631         art->regionid = RGN_TYPE_WINDOW;
632         art->init = graph_main_area_init;
633         art->draw = graph_main_area_draw;
634         art->listener = graph_region_listener;
635         art->keymapflag = ED_KEYMAP_VIEW2D | ED_KEYMAP_MARKERS | ED_KEYMAP_ANIMATION | ED_KEYMAP_FRAMES;
636
637         BLI_addhead(&st->regiontypes, art);
638         
639         /* regions: header */
640         art = MEM_callocN(sizeof(ARegionType), "spacetype graphedit region");
641         art->regionid = RGN_TYPE_HEADER;
642         art->prefsizey = HEADERY;
643         art->keymapflag = ED_KEYMAP_UI | ED_KEYMAP_VIEW2D | ED_KEYMAP_FRAMES | ED_KEYMAP_HEADER;
644         art->listener = graph_region_listener;
645         art->init = graph_header_area_init;
646         art->draw = graph_header_area_draw;
647         
648         BLI_addhead(&st->regiontypes, art);
649         
650         /* regions: channels */
651         art = MEM_callocN(sizeof(ARegionType), "spacetype graphedit region");
652         art->regionid = RGN_TYPE_CHANNELS;
653         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 */
654         art->keymapflag = ED_KEYMAP_UI | ED_KEYMAP_VIEW2D | ED_KEYMAP_FRAMES;
655         art->listener = graph_region_listener;
656         art->init = graph_channel_area_init;
657         art->draw = graph_channel_area_draw;
658         
659         BLI_addhead(&st->regiontypes, art);
660         
661         /* regions: UI buttons */
662         art = MEM_callocN(sizeof(ARegionType), "spacetype graphedit region");
663         art->regionid = RGN_TYPE_UI;
664         art->prefsizex = 200;
665         art->keymapflag = ED_KEYMAP_UI;
666         art->listener = graph_region_listener;
667         art->init = graph_buttons_area_init;
668         art->draw = graph_buttons_area_draw;
669         
670         BLI_addhead(&st->regiontypes, art);
671
672         graph_buttons_register(art);
673         
674         BKE_spacetype_register(st);
675 }
676