Merge branch 'master' into blender2.8
[blender.git] / source / blender / editors / space_time / space_time.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_time/space_time.c
28  *  \ingroup sptime
29  */
30
31
32 #include <string.h>
33 #include <stdio.h>
34
35 #include "DNA_cachefile_types.h"
36 #include "DNA_constraint_types.h"
37 #include "DNA_gpencil_types.h"
38 #include "DNA_modifier_types.h"
39 #include "DNA_object_types.h"
40 #include "DNA_scene_types.h"
41
42 #include "MEM_guardedalloc.h"
43
44 #include "BLI_blenlib.h"
45 #include "BLI_dlrbTree.h"
46 #include "BLI_utildefines.h"
47
48 #include "BKE_constraint.h"
49 #include "BKE_context.h"
50 #include "BKE_main.h"
51 #include "BKE_modifier.h"
52 #include "BKE_screen.h"
53
54 #include "ED_anim_api.h"
55 #include "ED_keyframes_draw.h"
56 #include "ED_screen.h"
57
58 #include "WM_api.h"
59 #include "WM_types.h"
60
61 #include "BIF_gl.h"
62 #include "BIF_glutil.h"
63
64 #include "UI_resources.h"
65 #include "UI_view2d.h"
66 #include "UI_interface.h"
67
68 #include "ED_space_api.h"
69 #include "ED_markers.h"
70
71 #include "time_intern.h"
72
73 /* ************************ main time area region *********************** */
74
75 static void time_draw_sfra_efra(Scene *scene, View2D *v2d)
76 {       
77         /* draw darkened area outside of active timeline 
78          * frame range used is preview range or scene range 
79          */
80         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
81         glEnable(GL_BLEND);
82         glColor4f(0.0f, 0.0f, 0.0f, 0.4f);
83                 
84         if (PSFRA < PEFRA) {
85                 glRectf(v2d->cur.xmin, v2d->cur.ymin, (float)PSFRA, v2d->cur.ymax);
86                 glRectf((float)PEFRA, v2d->cur.ymin, v2d->cur.xmax, v2d->cur.ymax);
87         }
88         else {
89                 glRectf(v2d->cur.xmin, v2d->cur.ymin, v2d->cur.xmax, v2d->cur.ymax);
90         }
91         glDisable(GL_BLEND);
92
93         UI_ThemeColorShade(TH_BACK, -60);
94         /* thin lines where the actual frames are */
95         fdrawline((float)PSFRA, v2d->cur.ymin, (float)PSFRA, v2d->cur.ymax);
96         fdrawline((float)PEFRA, v2d->cur.ymin, (float)PEFRA, v2d->cur.ymax);
97 }
98
99 static void time_cache_free(SpaceTime *stime)
100 {
101         SpaceTimeCache *stc;
102         
103         for (stc = stime->caches.first; stc; stc = stc->next) {
104                 if (stc->array) {
105                         MEM_freeN(stc->array);
106                         stc->array = NULL;
107                 }
108         }
109         
110         BLI_freelistN(&stime->caches);
111 }
112
113 static void time_cache_refresh(SpaceTime *stime)
114 {
115         /* Free previous caches to indicate full refresh */
116         time_cache_free(stime);
117 }
118
119 /* helper function - find actkeycolumn that occurs on cframe, or the nearest one if not found */
120 static ActKeyColumn *time_cfra_find_ak(ActKeyColumn *ak, float cframe)
121 {
122         ActKeyColumn *akn = NULL;
123         
124         /* sanity checks */
125         if (ak == NULL)
126                 return NULL;
127         
128         /* check if this is a match, or whether it is in some subtree */
129         if (cframe < ak->cfra)
130                 akn = time_cfra_find_ak(ak->left, cframe);
131         else if (cframe > ak->cfra)
132                 akn = time_cfra_find_ak(ak->right, cframe);
133                 
134         /* if no match found (or found match), just use the current one */
135         if (akn == NULL)
136                 return ak;
137         else
138                 return akn;
139 }
140
141 /* helper for time_draw_keyframes() */
142 static void time_draw_idblock_keyframes(View2D *v2d, ID *id, short onlysel)
143 {
144         bDopeSheet ads = {NULL};
145         DLRBT_Tree keys;
146         ActKeyColumn *ak;
147         
148         float fac1 = (GS(id->name) == ID_GD) ? 0.8f : 0.6f; /* draw GPencil keys taller, to help distinguish them */
149         float fac2 = 1.0f - fac1;
150         
151         float ymin = v2d->tot.ymin;
152         float ymax = v2d->tot.ymax * fac1 + ymin * fac2;
153         
154         /* init binarytree-list for getting keyframes */
155         BLI_dlrbTree_init(&keys);
156         
157         /* init dopesheet settings */
158         if (onlysel)
159                 ads.filterflag |= ADS_FILTER_ONLYSEL;
160
161         /* populate tree with keyframe nodes */
162         switch (GS(id->name)) {
163                 case ID_SCE:
164                         scene_to_keylist(&ads, (Scene *)id, &keys, NULL);
165                         break;
166                 case ID_OB:
167                         ob_to_keylist(&ads, (Object *)id, &keys, NULL);
168                         break;
169                 case ID_GD:
170                         gpencil_to_keylist(&ads, (bGPdata *)id, &keys);
171                         break;
172                 case ID_CF:
173                         cachefile_to_keylist(&ads, (CacheFile *)id, &keys, NULL);
174                         break;
175         }
176                 
177         /* build linked-list for searching */
178         BLI_dlrbTree_linkedlist_sync(&keys);
179         
180         /* start drawing keyframes 
181          *      - we use the binary-search capabilities of the tree to only start from 
182          *        the first visible keyframe (last one can then be easily checked)
183          *      - draw within a single GL block to be faster
184          */
185         glBegin(GL_LINES);
186         for (ak = time_cfra_find_ak(keys.root, v2d->cur.xmin);
187              (ak) && (ak->cfra <= v2d->cur.xmax);
188              ak = ak->next)
189         {
190                 glVertex2f(ak->cfra, ymin);
191                 glVertex2f(ak->cfra, ymax);
192         }
193         glEnd(); // GL_LINES
194                 
195         /* free temp stuff */
196         BLI_dlrbTree_free(&keys);
197 }
198
199 static void time_draw_caches_keyframes(Main *bmain, Scene *scene, View2D *v2d, bool onlysel)
200 {
201         CacheFile *cache_file;
202
203         for (cache_file = bmain->cachefiles.first;
204              cache_file;
205              cache_file = cache_file->id.next)
206         {
207                 cache_file->draw_flag &= ~CACHEFILE_KEYFRAME_DRAWN;
208         }
209
210         for (Base *base = scene->base.first; base; base = base->next) {
211                 Object *ob = base->object;
212
213                 ModifierData *md = modifiers_findByType(ob, eModifierType_MeshSequenceCache);
214
215                 if (md) {
216                         MeshSeqCacheModifierData *mcmd = (MeshSeqCacheModifierData *)md;
217
218                         cache_file = mcmd->cache_file;
219
220                         if (!cache_file || (cache_file->draw_flag & CACHEFILE_KEYFRAME_DRAWN) != 0) {
221                                 continue;
222                         }
223
224                         cache_file->draw_flag |= CACHEFILE_KEYFRAME_DRAWN;
225
226                         time_draw_idblock_keyframes(v2d, (ID *)cache_file, onlysel);
227                 }
228
229                 for (bConstraint *con = ob->constraints.first; con; con = con->next) {
230                         if (con->type != CONSTRAINT_TYPE_TRANSFORM_CACHE) {
231                                 continue;
232                         }
233
234                         bTransformCacheConstraint *data = con->data;
235
236                         cache_file = data->cache_file;
237
238                         if (!cache_file || (cache_file->draw_flag & CACHEFILE_KEYFRAME_DRAWN) != 0) {
239                                 continue;
240                         }
241
242                         cache_file->draw_flag |= CACHEFILE_KEYFRAME_DRAWN;
243
244                         time_draw_idblock_keyframes(v2d, (ID *)cache_file, onlysel);
245                 }
246         }
247 }
248
249 /* draw keyframe lines for timeline */
250 static void time_draw_keyframes(const bContext *C, ARegion *ar)
251 {
252         Scene *scene = CTX_data_scene(C);
253         Object *ob = CTX_data_active_object(C);
254         View2D *v2d = &ar->v2d;
255         bool onlysel = ((scene->flag & SCE_KEYS_NO_SELONLY) == 0);
256         
257         /* set this for all keyframe lines once and for all */
258         glLineWidth(1.0);
259
260         /* draw cache files keyframes (if available) */
261         UI_ThemeColor(TH_TIME_KEYFRAME);
262         time_draw_caches_keyframes(CTX_data_main(C), scene, v2d, onlysel);
263
264         /* draw grease pencil keyframes (if available) */       
265         UI_ThemeColor(TH_TIME_GP_KEYFRAME);
266         if (scene->gpd) {
267                 time_draw_idblock_keyframes(v2d, (ID *)scene->gpd, onlysel);
268         }
269         if (ob && ob->gpd) {
270                 time_draw_idblock_keyframes(v2d, (ID *)ob->gpd, onlysel);
271         }
272         
273         /* draw scene keyframes first 
274          *      - don't try to do this when only drawing active/selected data keyframes,
275          *        since this can become quite slow
276          */
277         if (onlysel == 0) {
278                 /* set draw color */
279                 UI_ThemeColorShade(TH_TIME_KEYFRAME, -50);
280                 time_draw_idblock_keyframes(v2d, (ID *)scene, onlysel);
281         }
282         
283         /* draw keyframes from selected objects 
284          *  - only do the active object if in posemode (i.e. showing only keyframes for the bones)
285          *    OR the onlysel flag was set, which means that only active object's keyframes should
286          *    be considered
287          */
288         UI_ThemeColor(TH_TIME_KEYFRAME);
289         
290         if (ob && ((ob->mode == OB_MODE_POSE) || onlysel)) {
291                 /* draw keyframes for active object only */
292                 time_draw_idblock_keyframes(v2d, (ID *)ob, onlysel);
293         }
294         else {
295                 bool active_done = false;
296                 
297                 /* draw keyframes from all selected objects */
298                 CTX_DATA_BEGIN (C, Object *, obsel, selected_objects)
299                 {
300                         /* last arg is 0, since onlysel doesn't apply here... */
301                         time_draw_idblock_keyframes(v2d, (ID *)obsel, 0);
302                         
303                         /* if this object is the active one, set flag so that we don't draw again */
304                         if (obsel == ob)
305                                 active_done = true;
306                 }
307                 CTX_DATA_END;
308                 
309                 /* if active object hasn't been done yet, draw it... */
310                 if (ob && (active_done == 0))
311                         time_draw_idblock_keyframes(v2d, (ID *)ob, 0);
312         }
313 }
314
315 /* ---------------- */
316
317 static void time_refresh(const bContext *UNUSED(C), ScrArea *sa)
318 {
319         /* find the main timeline region and refresh cache display*/
320         ARegion *ar = BKE_area_find_region_type(sa, RGN_TYPE_WINDOW);
321         if (ar) {
322                 SpaceTime *stime = (SpaceTime *)sa->spacedata.first;
323                 time_cache_refresh(stime);
324         }
325 }
326
327 /* editor level listener */
328 static void time_listener(bScreen *UNUSED(sc), ScrArea *sa, wmNotifier *wmn)
329 {
330
331         /* mainly for updating cache display */
332         switch (wmn->category) {
333                 case NC_OBJECT:
334                 {
335                         switch (wmn->data) {
336                                 case ND_BONE_SELECT:
337                                 case ND_BONE_ACTIVE:
338                                 case ND_POINTCACHE:
339                                 case ND_MODIFIER:
340                                 case ND_KEYS:
341                                         ED_area_tag_refresh(sa);
342                                         ED_area_tag_redraw(sa);
343                                         break;
344                         }
345                         break;
346                 }
347                 case NC_SCENE:
348                 {
349                         switch (wmn->data) {
350                                 case ND_RENDER_RESULT:
351                                         ED_area_tag_redraw(sa);
352                                         break;
353                                 case ND_OB_ACTIVE:
354                                 case ND_FRAME:
355                                         ED_area_tag_refresh(sa);
356                                         break;
357                                 case ND_FRAME_RANGE:
358                                 {
359                                         ARegion *ar;
360                                         Scene *scene = wmn->reference;
361
362                                         for (ar = sa->regionbase.first; ar; ar = ar->next) {
363                                                 if (ar->regiontype == RGN_TYPE_WINDOW) {
364                                                         ar->v2d.tot.xmin = (float)(SFRA - 4);
365                                                         ar->v2d.tot.xmax = (float)(EFRA + 4);
366                                                         break;
367                                                 }
368                                         }
369                                         break;
370                                 }
371                         }
372                         break;
373                 }
374                 case NC_SPACE:
375                 {
376                         switch (wmn->data) {
377                                 case ND_SPACE_CHANGED:
378                                         ED_area_tag_refresh(sa);
379                                         break;
380                         }
381                         break;
382                 }
383                 case NC_WM:
384                 {
385                         switch (wmn->data) {
386                                 case ND_FILEREAD:
387                                         ED_area_tag_refresh(sa);
388                                         break;
389                         }
390                         break;
391                 }
392         }
393 }
394
395 /* ---------------- */
396
397 /* add handlers, stuff you only do once or on area/region changes */
398 static void time_main_region_init(wmWindowManager *wm, ARegion *ar)
399 {
400         wmKeyMap *keymap;
401         
402         UI_view2d_region_reinit(&ar->v2d, V2D_COMMONVIEW_CUSTOM, ar->winx, ar->winy);
403         
404         /* own keymap */
405         keymap = WM_keymap_find(wm->defaultconf, "Timeline", SPACE_TIME, 0);
406         WM_event_add_keymap_handler_bb(&ar->handlers, keymap, &ar->v2d.mask, &ar->winrct);
407 }
408
409 static void time_main_region_draw(const bContext *C, ARegion *ar)
410 {
411         /* draw entirely, view changes should be handled here */
412         Scene *scene = CTX_data_scene(C);
413         SpaceTime *stime = CTX_wm_space_time(C);
414         View2D *v2d = &ar->v2d;
415         View2DGrid *grid;
416         View2DScrollers *scrollers;
417         int unit, flag = 0;
418         
419         /* clear and setup matrix */
420         UI_ThemeClearColor(TH_BACK);
421         glClear(GL_COLOR_BUFFER_BIT);
422         
423         UI_view2d_view_ortho(v2d);
424         
425         /* grid */
426         unit = (stime->flag & TIME_DRAWFRAMES) ? V2D_UNIT_FRAMES : V2D_UNIT_SECONDS;
427         grid = UI_view2d_grid_calc(scene, v2d, unit, V2D_GRID_CLAMP, V2D_ARG_DUMMY, V2D_ARG_DUMMY, ar->winx, ar->winy);
428         UI_view2d_grid_draw(v2d, grid, (V2D_VERTICAL_LINES | V2D_VERTICAL_AXIS));
429         UI_view2d_grid_free(grid);
430         
431         ED_region_draw_cb_draw(C, ar, REGION_DRAW_PRE_VIEW);
432
433         /* start and end frame */
434         time_draw_sfra_efra(scene, v2d);
435         
436         /* current frame */
437         flag = DRAWCFRA_WIDE; /* this is only really needed on frames where there's a keyframe, but this will do... */
438         if ((stime->flag & TIME_DRAWFRAMES) == 0)  flag |= DRAWCFRA_UNIT_SECONDS;
439         if (stime->flag & TIME_CFRA_NUM)           flag |= DRAWCFRA_SHOW_NUMBOX;
440         ANIM_draw_cfra(C, v2d, flag);
441         
442         UI_view2d_view_ortho(v2d);
443         
444         /* keyframes */
445         time_draw_keyframes(C, ar);
446         
447         /* markers */
448         UI_view2d_view_orthoSpecial(ar, v2d, 1);
449         ED_markers_draw(C, 0);
450         
451         /* callback */
452         UI_view2d_view_ortho(v2d);
453         ED_region_draw_cb_draw(C, ar, REGION_DRAW_POST_VIEW);
454
455         /* reset view matrix */
456         UI_view2d_view_restore(C);
457         
458         /* scrollers */
459         scrollers = UI_view2d_scrollers_calc(C, v2d, unit, V2D_GRID_CLAMP, V2D_ARG_DUMMY, V2D_ARG_DUMMY);
460         UI_view2d_scrollers_draw(C, v2d, scrollers);
461         UI_view2d_scrollers_free(scrollers);
462 }
463
464 static void time_main_region_listener(bScreen *UNUSED(sc), ScrArea *UNUSED(sa), ARegion *ar, wmNotifier *wmn)
465 {
466         /* context changes */
467         switch (wmn->category) {
468                 case NC_SPACE:
469                         if (wmn->data == ND_SPACE_TIME)
470                                 ED_region_tag_redraw(ar);
471                         break;
472
473                 case NC_ANIMATION:
474                         ED_region_tag_redraw(ar);
475                         break;
476                 
477                 case NC_SCENE:
478                         switch (wmn->data) {
479                                 case ND_OB_SELECT:
480                                 case ND_OB_ACTIVE:
481                                 case ND_FRAME:
482                                 case ND_FRAME_RANGE:
483                                 case ND_KEYINGSET:
484                                 case ND_RENDER_OPTIONS:
485                                         ED_region_tag_redraw(ar);
486                                         break;
487                         }
488                         break;
489                 case NC_GPENCIL:
490                         if (wmn->data == ND_DATA)
491                                 ED_region_tag_redraw(ar);
492                         break;
493         }
494 }
495
496 /* ************************ header time area region *********************** */
497
498 /* add handlers, stuff you only do once or on area/region changes */
499 static void time_header_region_init(wmWindowManager *UNUSED(wm), ARegion *ar)
500 {
501         ED_region_header_init(ar);
502 }
503
504 static void time_header_region_draw(const bContext *C, ARegion *ar)
505 {
506         ED_region_header(C, ar);
507 }
508
509 static void time_header_region_listener(bScreen *UNUSED(sc), ScrArea *UNUSED(sa), ARegion *ar, wmNotifier *wmn)
510 {
511         /* context changes */
512         switch (wmn->category) {
513                 case NC_SCREEN:
514                 {
515                         if (wmn->data == ND_ANIMPLAY)
516                                 ED_region_tag_redraw(ar);
517                         break;
518                 }
519                 case NC_SCENE:
520                 {
521                         switch (wmn->data) {
522                                 case ND_RENDER_RESULT:
523                                 case ND_OB_SELECT:
524                                 case ND_FRAME:
525                                 case ND_FRAME_RANGE:
526                                 case ND_KEYINGSET:
527                                 case ND_RENDER_OPTIONS:
528                                         ED_region_tag_redraw(ar);
529                                         break;
530                         }
531                         break;
532                 }
533                 case NC_SPACE:
534                 {
535                         if (wmn->data == ND_SPACE_TIME)
536                                 ED_region_tag_redraw(ar);
537                         break;
538                 }
539         }
540 }
541
542 /* ******************** default callbacks for time space ***************** */
543
544 static SpaceLink *time_new(const bContext *C)
545 {
546         Scene *scene = CTX_data_scene(C);
547         ARegion *ar;
548         SpaceTime *stime;
549
550         stime = MEM_callocN(sizeof(SpaceTime), "inittime");
551
552         stime->spacetype = SPACE_TIME;
553         stime->flag |= TIME_DRAWFRAMES;
554
555         /* header */
556         ar = MEM_callocN(sizeof(ARegion), "header for time");
557         
558         BLI_addtail(&stime->regionbase, ar);
559         ar->regiontype = RGN_TYPE_HEADER;
560         ar->alignment = RGN_ALIGN_BOTTOM;
561         
562         /* main region */
563         ar = MEM_callocN(sizeof(ARegion), "main region for time");
564         
565         BLI_addtail(&stime->regionbase, ar);
566         ar->regiontype = RGN_TYPE_WINDOW;
567         
568         ar->v2d.tot.xmin = (float)(SFRA - 4);
569         ar->v2d.tot.ymin = 0.0f;
570         ar->v2d.tot.xmax = (float)(EFRA + 4);
571         ar->v2d.tot.ymax = 50.0f;
572         
573         ar->v2d.cur = ar->v2d.tot;
574
575         ar->v2d.min[0] = 1.0f;
576         ar->v2d.min[1] = 50.0f;
577
578         ar->v2d.max[0] = MAXFRAMEF;
579         ar->v2d.max[1] = 50.0;
580
581         ar->v2d.minzoom = 0.1f;
582         ar->v2d.maxzoom = 10.0;
583
584         ar->v2d.scroll |= (V2D_SCROLL_BOTTOM | V2D_SCROLL_SCALE_HORIZONTAL);
585         ar->v2d.align |= V2D_ALIGN_NO_NEG_Y;
586         ar->v2d.keepofs |= V2D_LOCKOFS_Y;
587         ar->v2d.keepzoom |= V2D_LOCKZOOM_Y;
588
589
590         return (SpaceLink *)stime;
591 }
592
593 /* not spacelink itself */
594 static void time_free(SpaceLink *sl)
595 {
596         SpaceTime *stime = (SpaceTime *)sl;
597         
598         time_cache_free(stime);
599 }
600 /* spacetype; init callback in ED_area_initialize() */
601 /* init is called to (re)initialize an existing editor (file read, screen changes) */
602 /* validate spacedata, add own area level handlers */
603 static void time_init(wmWindowManager *UNUSED(wm), ScrArea *sa)
604 {
605         SpaceTime *stime = (SpaceTime *)sa->spacedata.first;
606         
607         time_cache_free(stime);
608         
609         /* enable all cache display */
610         stime->cache_display |= TIME_CACHE_DISPLAY;
611         stime->cache_display |= (TIME_CACHE_SOFTBODY | TIME_CACHE_PARTICLES);
612         stime->cache_display |= (TIME_CACHE_CLOTH | TIME_CACHE_SMOKE | TIME_CACHE_DYNAMICPAINT);
613         stime->cache_display |= TIME_CACHE_RIGIDBODY;
614 }
615
616 static SpaceLink *time_duplicate(SpaceLink *sl)
617 {
618         SpaceTime *stime = (SpaceTime *)sl;
619         SpaceTime *stimen = MEM_dupallocN(stime);
620         
621         BLI_listbase_clear(&stimen->caches);
622         
623         return (SpaceLink *)stimen;
624 }
625
626 /* only called once, from space_api/spacetypes.c */
627 /* it defines all callbacks to maintain spaces */
628 void ED_spacetype_time(void)
629 {
630         SpaceType *st = MEM_callocN(sizeof(SpaceType), "spacetype time");
631         ARegionType *art;
632         
633         st->spaceid = SPACE_TIME;
634         strncpy(st->name, "Timeline", BKE_ST_MAXNAME);
635         
636         st->new = time_new;
637         st->free = time_free;
638         st->init = time_init;
639         st->duplicate = time_duplicate;
640         st->operatortypes = time_operatortypes;
641         st->keymap = NULL;
642         st->listener = time_listener;
643         st->refresh = time_refresh;
644         
645         /* regions: main window */
646         art = MEM_callocN(sizeof(ARegionType), "spacetype time region");
647         art->regionid = RGN_TYPE_WINDOW;
648         art->keymapflag = ED_KEYMAP_VIEW2D | ED_KEYMAP_MARKERS | ED_KEYMAP_ANIMATION | ED_KEYMAP_FRAMES;
649         
650         art->init = time_main_region_init;
651         art->draw = time_main_region_draw;
652         art->listener = time_main_region_listener;
653         art->keymap = time_keymap;
654         BLI_addhead(&st->regiontypes, art);
655         
656         /* regions: header */
657         art = MEM_callocN(sizeof(ARegionType), "spacetype time region");
658         art->regionid = RGN_TYPE_HEADER;
659         art->prefsizey = HEADERY;
660         art->keymapflag = ED_KEYMAP_UI | ED_KEYMAP_VIEW2D | ED_KEYMAP_FRAMES | ED_KEYMAP_HEADER;
661         
662         art->init = time_header_region_init;
663         art->draw = time_header_region_draw;
664         art->listener = time_header_region_listener;
665         BLI_addhead(&st->regiontypes, art);
666                 
667         BKE_spacetype_register(st);
668 }