Cleanup: style, use braces for editors
[blender.git] / source / blender / editors / space_action / space_action.c
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  *
16  * The Original Code is Copyright (C) 2008 Blender Foundation.
17  * All rights reserved.
18  */
19
20 /** \file
21  * \ingroup spaction
22  */
23
24 #include <string.h>
25 #include <stdio.h>
26
27 #include "DNA_action_types.h"
28 #include "DNA_collection_types.h"
29 #include "DNA_object_types.h"
30 #include "DNA_scene_types.h"
31
32 #include "MEM_guardedalloc.h"
33
34 #include "BLI_blenlib.h"
35 #include "BLI_utildefines.h"
36
37 #include "BKE_context.h"
38 #include "BKE_screen.h"
39
40 #include "RNA_access.h"
41 #include "RNA_define.h"
42 #include "RNA_enum_types.h"
43
44 #include "WM_api.h"
45 #include "WM_types.h"
46 #include "WM_message.h"
47
48 #include "UI_resources.h"
49 #include "UI_view2d.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 "action_intern.h" /* own include */
57 #include "GPU_framebuffer.h"
58
59 /* ******************** default callbacks for action space ***************** */
60
61 static SpaceLink *action_new(const ScrArea *sa, const Scene *scene)
62 {
63   SpaceAction *saction;
64   ARegion *ar;
65
66   saction = MEM_callocN(sizeof(SpaceAction), "initaction");
67   saction->spacetype = SPACE_ACTION;
68
69   saction->autosnap = SACTSNAP_FRAME;
70   saction->mode = SACTCONT_DOPESHEET;
71   saction->mode_prev = SACTCONT_DOPESHEET;
72   saction->flag = SACTION_SHOW_INTERPOLATION;
73
74   saction->ads.filterflag |= ADS_FILTER_SUMMARY;
75
76   /* enable all cache display */
77   saction->cache_display |= TIME_CACHE_DISPLAY;
78   saction->cache_display |= (TIME_CACHE_SOFTBODY | TIME_CACHE_PARTICLES);
79   saction->cache_display |= (TIME_CACHE_CLOTH | TIME_CACHE_SMOKE | TIME_CACHE_DYNAMICPAINT);
80   saction->cache_display |= TIME_CACHE_RIGIDBODY;
81
82   /* header */
83   ar = MEM_callocN(sizeof(ARegion), "header for action");
84
85   BLI_addtail(&saction->regionbase, ar);
86   ar->regiontype = RGN_TYPE_HEADER;
87   ar->alignment = (U.uiflag & USER_HEADER_BOTTOM) ? RGN_ALIGN_BOTTOM : RGN_ALIGN_TOP;
88
89   /* channel list region */
90   ar = MEM_callocN(sizeof(ARegion), "channel region for action");
91   BLI_addtail(&saction->regionbase, ar);
92   ar->regiontype = RGN_TYPE_CHANNELS;
93   ar->alignment = RGN_ALIGN_LEFT;
94
95   /* only need to set scroll settings, as this will use 'listview' v2d configuration */
96   ar->v2d.scroll = V2D_SCROLL_BOTTOM;
97   ar->v2d.flag = V2D_VIEWSYNC_AREA_VERTICAL;
98
99   /* ui buttons */
100   ar = MEM_callocN(sizeof(ARegion), "buttons region for action");
101
102   BLI_addtail(&saction->regionbase, ar);
103   ar->regiontype = RGN_TYPE_UI;
104   ar->alignment = RGN_ALIGN_RIGHT;
105   ar->flag = RGN_FLAG_HIDDEN;
106
107   /* main region */
108   ar = MEM_callocN(sizeof(ARegion), "main region for action");
109
110   BLI_addtail(&saction->regionbase, ar);
111   ar->regiontype = RGN_TYPE_WINDOW;
112
113   ar->v2d.tot.xmin = (float)(SFRA - 10);
114   ar->v2d.tot.ymin = (float)(-sa->winy) / 3.0f;
115   ar->v2d.tot.xmax = (float)(EFRA + 10);
116   ar->v2d.tot.ymax = 0.0f;
117
118   ar->v2d.cur = ar->v2d.tot;
119
120   ar->v2d.min[0] = 0.0f;
121   ar->v2d.min[1] = 0.0f;
122
123   ar->v2d.max[0] = MAXFRAMEF;
124   ar->v2d.max[1] = FLT_MAX;
125
126   ar->v2d.minzoom = 0.01f;
127   ar->v2d.maxzoom = 50;
128   ar->v2d.scroll = (V2D_SCROLL_BOTTOM | V2D_SCROLL_SCALE_HORIZONTAL);
129   ar->v2d.scroll |= (V2D_SCROLL_RIGHT);
130   ar->v2d.keepzoom = V2D_LOCKZOOM_Y;
131   ar->v2d.keepofs = V2D_KEEPOFS_Y;
132   ar->v2d.align = V2D_ALIGN_NO_POS_Y;
133   ar->v2d.flag = V2D_VIEWSYNC_AREA_VERTICAL;
134
135   return (SpaceLink *)saction;
136 }
137
138 /* not spacelink itself */
139 static void action_free(SpaceLink *UNUSED(sl))
140 {
141   //  SpaceAction *saction = (SpaceAction *) sl;
142 }
143
144 /* spacetype; init callback */
145 static void action_init(struct wmWindowManager *UNUSED(wm), ScrArea *sa)
146 {
147   SpaceAction *saction = sa->spacedata.first;
148   saction->runtime.flag |= SACTION_RUNTIME_FLAG_NEED_CHAN_SYNC;
149 }
150
151 static SpaceLink *action_duplicate(SpaceLink *sl)
152 {
153   SpaceAction *sactionn = MEM_dupallocN(sl);
154
155   /* clear or remove stuff from old */
156
157   return (SpaceLink *)sactionn;
158 }
159
160 /* add handlers, stuff you only do once or on area/region changes */
161 static void action_main_region_init(wmWindowManager *wm, ARegion *ar)
162 {
163   wmKeyMap *keymap;
164
165   UI_view2d_region_reinit(&ar->v2d, V2D_COMMONVIEW_CUSTOM, ar->winx, ar->winy);
166
167   /* own keymap */
168   keymap = WM_keymap_ensure(wm->defaultconf, "Dopesheet", SPACE_ACTION, 0);
169   WM_event_add_keymap_handler_bb(&ar->handlers, keymap, &ar->v2d.mask, &ar->winrct);
170   keymap = WM_keymap_ensure(wm->defaultconf, "Dopesheet Generic", SPACE_ACTION, 0);
171   WM_event_add_keymap_handler(&ar->handlers, keymap);
172 }
173
174 static void action_main_region_draw(const bContext *C, ARegion *ar)
175 {
176   /* draw entirely, view changes should be handled here */
177   SpaceAction *saction = CTX_wm_space_action(C);
178   Scene *scene = CTX_data_scene(C);
179   Object *obact = CTX_data_active_object(C);
180   bAnimContext ac;
181   View2D *v2d = &ar->v2d;
182   View2DGrid *grid;
183   View2DScrollers *scrollers;
184   short marker_flag = 0;
185   short cfra_flag = 0;
186   short unit = 0;
187
188   /* clear and setup matrix */
189   UI_ThemeClearColor(TH_BACK);
190   GPU_clear(GPU_COLOR_BIT);
191
192   UI_view2d_view_ortho(v2d);
193
194   /* time grid */
195   unit = (saction->flag & SACTION_DRAWTIME) ? V2D_UNIT_SECONDS : V2D_UNIT_FRAMES;
196   grid = UI_view2d_grid_calc(CTX_data_scene(C),
197                              v2d,
198                              unit,
199                              V2D_GRID_CLAMP,
200                              V2D_ARG_DUMMY,
201                              V2D_ARG_DUMMY,
202                              ar->winx,
203                              ar->winy);
204   UI_view2d_grid_draw(v2d, grid, V2D_GRIDLINES_ALL);
205   UI_view2d_grid_free(grid);
206
207   ED_region_draw_cb_draw(C, ar, REGION_DRAW_PRE_VIEW);
208
209   /* start and end frame */
210   ANIM_draw_framerange(scene, v2d);
211
212   /* data */
213   if (ANIM_animdata_get_context(C, &ac)) {
214     draw_channel_strips(&ac, saction, ar);
215   }
216
217   /* current frame */
218   if (saction->flag & SACTION_DRAWTIME) {
219     cfra_flag |= DRAWCFRA_UNIT_SECONDS;
220   }
221   ANIM_draw_cfra(C, v2d, cfra_flag);
222
223   /* markers */
224   UI_view2d_view_orthoSpecial(ar, v2d, 1);
225
226   marker_flag = ((ac.markers && (ac.markers != &ac.scene->markers)) ? DRAW_MARKERS_LOCAL : 0) |
227                 DRAW_MARKERS_MARGIN;
228   if (saction->flag & SACTION_SHOW_MARKER_LINES) {
229     marker_flag |= DRAW_MARKERS_LINES;
230   }
231   ED_markers_draw(C, marker_flag);
232
233   /* caches */
234   if (saction->mode == SACTCONT_TIMELINE) {
235     timeline_draw_cache(saction, obact, scene);
236   }
237
238   /* preview range */
239   UI_view2d_view_ortho(v2d);
240   ANIM_draw_previewrange(C, v2d, 0);
241
242   /* callback */
243   UI_view2d_view_ortho(v2d);
244   ED_region_draw_cb_draw(C, ar, REGION_DRAW_POST_VIEW);
245
246   /* reset view matrix */
247   UI_view2d_view_restore(C);
248
249   /* scrollers */
250   scrollers = UI_view2d_scrollers_calc(
251       C, v2d, NULL, unit, V2D_GRID_CLAMP, V2D_ARG_DUMMY, V2D_ARG_DUMMY);
252   UI_view2d_scrollers_draw(C, v2d, scrollers);
253   UI_view2d_scrollers_free(scrollers);
254
255   /* draw current frame number-indicator on top of scrollers */
256   if ((saction->flag & SACTION_NODRAWCFRANUM) == 0) {
257     UI_view2d_view_orthoSpecial(ar, v2d, 1);
258     ANIM_draw_cfra_number(C, v2d, cfra_flag);
259   }
260 }
261
262 /* add handlers, stuff you only do once or on area/region changes */
263 static void action_channel_region_init(wmWindowManager *wm, ARegion *ar)
264 {
265   wmKeyMap *keymap;
266
267   /* ensure the 2d view sync works - main region has bottom scroller */
268   ar->v2d.scroll = V2D_SCROLL_BOTTOM;
269
270   UI_view2d_region_reinit(&ar->v2d, V2D_COMMONVIEW_LIST, ar->winx, ar->winy);
271
272   /* own keymap */
273   keymap = WM_keymap_ensure(wm->defaultconf, "Animation Channels", 0, 0);
274   WM_event_add_keymap_handler_bb(&ar->handlers, keymap, &ar->v2d.mask, &ar->winrct);
275
276   keymap = WM_keymap_ensure(wm->defaultconf, "Dopesheet Generic", SPACE_ACTION, 0);
277   WM_event_add_keymap_handler(&ar->handlers, keymap);
278 }
279
280 static void action_channel_region_draw(const bContext *C, ARegion *ar)
281 {
282   /* draw entirely, view changes should be handled here */
283   bAnimContext ac;
284   View2D *v2d = &ar->v2d;
285
286   /* clear and setup matrix */
287   UI_ThemeClearColor(TH_BACK);
288   GPU_clear(GPU_COLOR_BIT);
289
290   UI_view2d_view_ortho(v2d);
291
292   /* data */
293   if (ANIM_animdata_get_context(C, &ac)) {
294     draw_channel_names((bContext *)C, &ac, ar);
295   }
296
297   /* reset view matrix */
298   UI_view2d_view_restore(C);
299
300   /* no scrollers here */
301 }
302
303 /* add handlers, stuff you only do once or on area/region changes */
304 static void action_header_region_init(wmWindowManager *UNUSED(wm), ARegion *ar)
305 {
306   ED_region_header_init(ar);
307 }
308
309 static void action_header_region_draw(const bContext *C, ARegion *ar)
310 {
311   ED_region_header(C, ar);
312 }
313
314 static void action_channel_region_listener(wmWindow *UNUSED(win),
315                                            ScrArea *UNUSED(sa),
316                                            ARegion *ar,
317                                            wmNotifier *wmn,
318                                            const Scene *UNUSED(scene))
319 {
320   /* context changes */
321   switch (wmn->category) {
322     case NC_ANIMATION:
323       ED_region_tag_redraw(ar);
324       break;
325     case NC_SCENE:
326       switch (wmn->data) {
327         case ND_OB_ACTIVE:
328         case ND_FRAME:
329           ED_region_tag_redraw(ar);
330           break;
331       }
332       break;
333     case NC_OBJECT:
334       switch (wmn->data) {
335         case ND_BONE_ACTIVE:
336         case ND_BONE_SELECT:
337         case ND_KEYS:
338           ED_region_tag_redraw(ar);
339           break;
340         case ND_MODIFIER:
341           if (wmn->action == NA_RENAME) {
342             ED_region_tag_redraw(ar);
343           }
344           break;
345       }
346       break;
347     case NC_GPENCIL:
348       if (ELEM(wmn->action, NA_RENAME, NA_SELECTED)) {
349         ED_region_tag_redraw(ar);
350       }
351       break;
352     case NC_ID:
353       if (wmn->action == NA_RENAME) {
354         ED_region_tag_redraw(ar);
355       }
356       break;
357     default:
358       if (wmn->data == ND_KEYS) {
359         ED_region_tag_redraw(ar);
360       }
361       break;
362   }
363 }
364
365 static void saction_channel_region_message_subscribe(const struct bContext *UNUSED(C),
366                                                      struct WorkSpace *UNUSED(workspace),
367                                                      struct Scene *UNUSED(scene),
368                                                      struct bScreen *screen,
369                                                      struct ScrArea *sa,
370                                                      struct ARegion *ar,
371                                                      struct wmMsgBus *mbus)
372 {
373   PointerRNA ptr;
374   RNA_pointer_create(&screen->id, &RNA_SpaceDopeSheetEditor, sa->spacedata.first, &ptr);
375
376   wmMsgSubscribeValue msg_sub_value_region_tag_redraw = {
377       .owner = ar,
378       .user_data = ar,
379       .notify = ED_region_do_msg_notify_tag_redraw,
380   };
381
382   /* All dopesheet filter settings, etc. affect the drawing of this editor,
383    * also same applies for all animation-related datatypes that may appear here,
384    * so just whitelist the entire structs for updates
385    */
386   {
387     wmMsgParams_RNA msg_key_params = {{{0}}};
388     StructRNA *type_array[] = {
389         &RNA_DopeSheet, /* dopesheet filters */
390
391         &RNA_ActionGroup, /* channel groups */
392
393         &RNA_FCurve, /* F-Curve */
394         &RNA_Keyframe,
395         &RNA_FCurveSample,
396
397         &RNA_GreasePencil, /* Grease Pencil */
398         &RNA_GPencilLayer,
399         &RNA_GPencilFrame,
400     };
401
402     for (int i = 0; i < ARRAY_SIZE(type_array); i++) {
403       msg_key_params.ptr.type = type_array[i];
404       WM_msg_subscribe_rna_params(
405           mbus, &msg_key_params, &msg_sub_value_region_tag_redraw, __func__);
406     }
407   }
408 }
409
410 static void action_main_region_listener(wmWindow *UNUSED(win),
411                                         ScrArea *UNUSED(sa),
412                                         ARegion *ar,
413                                         wmNotifier *wmn,
414                                         const Scene *UNUSED(scene))
415 {
416   /* context changes */
417   switch (wmn->category) {
418     case NC_ANIMATION:
419       ED_region_tag_redraw(ar);
420       break;
421     case NC_SCENE:
422       switch (wmn->data) {
423         case ND_RENDER_OPTIONS:
424         case ND_OB_ACTIVE:
425         case ND_FRAME:
426         case ND_FRAME_RANGE:
427         case ND_MARKERS:
428           ED_region_tag_redraw(ar);
429           break;
430       }
431       break;
432     case NC_OBJECT:
433       switch (wmn->data) {
434         case ND_TRANSFORM:
435           /* moving object shouldn't need to redraw action */
436           break;
437         case ND_BONE_ACTIVE:
438         case ND_BONE_SELECT:
439         case ND_KEYS:
440           ED_region_tag_redraw(ar);
441           break;
442       }
443       break;
444     case NC_NODE:
445       switch (wmn->action) {
446         case NA_EDITED:
447           ED_region_tag_redraw(ar);
448           break;
449       }
450       break;
451     case NC_ID:
452       if (wmn->action == NA_RENAME) {
453         ED_region_tag_redraw(ar);
454       }
455       break;
456     case NC_SCREEN:
457       if (ELEM(wmn->data, ND_LAYER)) {
458         ED_region_tag_redraw(ar);
459       }
460       break;
461     default:
462       if (wmn->data == ND_KEYS) {
463         ED_region_tag_redraw(ar);
464       }
465       break;
466   }
467 }
468
469 static void saction_main_region_message_subscribe(const struct bContext *C,
470                                                   struct WorkSpace *workspace,
471                                                   struct Scene *scene,
472                                                   struct bScreen *screen,
473                                                   struct ScrArea *sa,
474                                                   struct ARegion *ar,
475                                                   struct wmMsgBus *mbus)
476 {
477   PointerRNA ptr;
478   RNA_pointer_create(&screen->id, &RNA_SpaceDopeSheetEditor, sa->spacedata.first, &ptr);
479
480   wmMsgSubscribeValue msg_sub_value_region_tag_redraw = {
481       .owner = ar,
482       .user_data = ar,
483       .notify = ED_region_do_msg_notify_tag_redraw,
484   };
485
486   /* Timeline depends on scene properties. */
487   {
488     bool use_preview = (scene->r.flag & SCER_PRV_RANGE);
489     extern PropertyRNA rna_Scene_frame_start;
490     extern PropertyRNA rna_Scene_frame_end;
491     extern PropertyRNA rna_Scene_frame_preview_start;
492     extern PropertyRNA rna_Scene_frame_preview_end;
493     extern PropertyRNA rna_Scene_use_preview_range;
494     extern PropertyRNA rna_Scene_frame_current;
495     const PropertyRNA *props[] = {
496         use_preview ? &rna_Scene_frame_preview_start : &rna_Scene_frame_start,
497         use_preview ? &rna_Scene_frame_preview_end : &rna_Scene_frame_end,
498         &rna_Scene_use_preview_range,
499         &rna_Scene_frame_current,
500     };
501
502     PointerRNA idptr;
503     RNA_id_pointer_create(&scene->id, &idptr);
504
505     for (int i = 0; i < ARRAY_SIZE(props); i++) {
506       WM_msg_subscribe_rna(mbus, &idptr, props[i], &msg_sub_value_region_tag_redraw, __func__);
507     }
508   }
509
510   /* Now run the general "channels region" one - since channels and main should be in sync */
511   saction_channel_region_message_subscribe(C, workspace, scene, screen, sa, ar, mbus);
512 }
513
514 /* editor level listener */
515 static void action_listener(wmWindow *UNUSED(win),
516                             ScrArea *sa,
517                             wmNotifier *wmn,
518                             Scene *UNUSED(scene))
519 {
520   SpaceAction *saction = (SpaceAction *)sa->spacedata.first;
521
522   /* context changes */
523   switch (wmn->category) {
524     case NC_GPENCIL:
525       /* only handle these events in GPencil mode for performance considerations */
526       if (saction->mode == SACTCONT_GPENCIL) {
527         if (wmn->action == NA_EDITED) {
528           ED_area_tag_redraw(sa);
529         }
530         else if (wmn->action == NA_SELECTED) {
531           saction->runtime.flag |= SACTION_RUNTIME_FLAG_NEED_CHAN_SYNC;
532           ED_area_tag_refresh(sa);
533         }
534       }
535       break;
536     case NC_ANIMATION:
537       /* for NLA tweakmode enter/exit, need complete refresh */
538       if (wmn->data == ND_NLA_ACTCHANGE) {
539         saction->runtime.flag |= SACTION_RUNTIME_FLAG_NEED_CHAN_SYNC;
540         ED_area_tag_refresh(sa);
541       }
542       /* autocolor only really needs to change when channels are added/removed,
543        * or previously hidden stuff appears
544        * (assume for now that if just adding these works, that will be fine)
545        */
546       else if (((wmn->data == ND_KEYFRAME) && ELEM(wmn->action, NA_ADDED, NA_REMOVED)) ||
547                ((wmn->data == ND_ANIMCHAN) && (wmn->action != NA_SELECTED))) {
548         ED_area_tag_refresh(sa);
549       }
550       /* for simple edits to the curve data though (or just plain selections),
551        * a simple redraw should work
552        * (see T39851 for an example of how this can go wrong)
553        */
554       else {
555         ED_area_tag_redraw(sa);
556       }
557       break;
558     case NC_SCENE:
559       switch (wmn->data) {
560         case ND_OB_ACTIVE:
561         case ND_OB_SELECT:
562           /* Selection changed, so force refresh to flush
563            * (needs flag set to do syncing). */
564           saction->runtime.flag |= SACTION_RUNTIME_FLAG_NEED_CHAN_SYNC;
565           ED_area_tag_refresh(sa);
566           break;
567         case ND_RENDER_RESULT:
568           ED_area_tag_redraw(sa);
569           break;
570         case ND_FRAME_RANGE:
571           for (ARegion *ar = sa->regionbase.first; ar; ar = ar->next) {
572             if (ar->regiontype == RGN_TYPE_WINDOW) {
573               Scene *scene = wmn->reference;
574               ar->v2d.tot.xmin = (float)(SFRA - 4);
575               ar->v2d.tot.xmax = (float)(EFRA + 4);
576               break;
577             }
578           }
579           break;
580         default:
581           if (saction->mode != SACTCONT_TIMELINE) {
582             /* Just redrawing the view will do. */
583             ED_area_tag_redraw(sa);
584           }
585           break;
586       }
587       break;
588     case NC_OBJECT:
589       switch (wmn->data) {
590         case ND_BONE_SELECT: /* Selection changed, so force refresh to flush
591                               * (needs flag set to do syncing). */
592         case ND_BONE_ACTIVE:
593           saction->runtime.flag |= SACTION_RUNTIME_FLAG_NEED_CHAN_SYNC;
594           ED_area_tag_refresh(sa);
595           break;
596         case ND_TRANSFORM:
597           /* moving object shouldn't need to redraw action */
598           break;
599         case ND_POINTCACHE:
600         case ND_MODIFIER:
601         case ND_PARTICLE:
602           /* only needed in timeline mode */
603           if (saction->mode == SACTCONT_TIMELINE) {
604             ED_area_tag_refresh(sa);
605             ED_area_tag_redraw(sa);
606           }
607           break;
608         default: /* just redrawing the view will do */
609           ED_area_tag_redraw(sa);
610           break;
611       }
612       break;
613     case NC_MASK:
614       if (saction->mode == SACTCONT_MASK) {
615         switch (wmn->data) {
616           case ND_DATA:
617             ED_area_tag_refresh(sa);
618             ED_area_tag_redraw(sa);
619             break;
620           default: /* just redrawing the view will do */
621             ED_area_tag_redraw(sa);
622             break;
623         }
624       }
625       break;
626     case NC_NODE:
627       if (wmn->action == NA_SELECTED) {
628         /* selection changed, so force refresh to flush (needs flag set to do syncing) */
629         saction->runtime.flag |= SACTION_RUNTIME_FLAG_NEED_CHAN_SYNC;
630         ED_area_tag_refresh(sa);
631       }
632       break;
633     case NC_SPACE:
634       switch (wmn->data) {
635         case ND_SPACE_DOPESHEET:
636           ED_area_tag_redraw(sa);
637           break;
638         case ND_SPACE_TIME:
639           ED_area_tag_redraw(sa);
640           break;
641         case ND_SPACE_CHANGED:
642           saction->runtime.flag |= SACTION_RUNTIME_FLAG_NEED_CHAN_SYNC;
643           ED_area_tag_refresh(sa);
644           break;
645       }
646       break;
647     case NC_WINDOW:
648       if (saction->runtime.flag & SACTION_RUNTIME_FLAG_NEED_CHAN_SYNC) {
649         /* force redraw/refresh after undo/redo - [#28962] */
650         ED_area_tag_refresh(sa);
651       }
652       break;
653     case NC_WM:
654       switch (wmn->data) {
655         case ND_FILEREAD:
656           ED_area_tag_refresh(sa);
657           break;
658       }
659       break;
660   }
661 }
662
663 static void action_header_region_listener(
664     wmWindow *UNUSED(win), ScrArea *sa, ARegion *ar, wmNotifier *wmn, const Scene *UNUSED(scene))
665 {
666   SpaceAction *saction = (SpaceAction *)sa->spacedata.first;
667
668   /* context changes */
669   switch (wmn->category) {
670     case NC_SCREEN:
671       if (saction->mode == SACTCONT_TIMELINE) {
672         if (wmn->data == ND_ANIMPLAY) {
673           ED_region_tag_redraw(ar);
674         }
675       }
676       break;
677     case NC_SCENE:
678       if (saction->mode == SACTCONT_TIMELINE) {
679         switch (wmn->data) {
680           case ND_RENDER_RESULT:
681           case ND_OB_SELECT:
682           case ND_FRAME:
683           case ND_FRAME_RANGE:
684           case ND_KEYINGSET:
685           case ND_RENDER_OPTIONS:
686             ED_region_tag_redraw(ar);
687             break;
688         }
689       }
690       else {
691         switch (wmn->data) {
692           case ND_OB_ACTIVE:
693             ED_region_tag_redraw(ar);
694             break;
695         }
696       }
697       break;
698     case NC_ID:
699       if (wmn->action == NA_RENAME) {
700         ED_region_tag_redraw(ar);
701       }
702       break;
703     case NC_ANIMATION:
704       switch (wmn->data) {
705         case ND_ANIMCHAN: /* set of visible animchannels changed */
706           /* NOTE: for now, this should usually just mean that the filters changed
707            *       It may be better if we had a dedicated flag for that though
708            */
709           ED_region_tag_redraw(ar);
710           break;
711
712         case ND_KEYFRAME: /* new keyframed added -> active action may have changed */
713           //saction->flag |= SACTION_TEMP_NEEDCHANSYNC;
714           ED_region_tag_redraw(ar);
715           break;
716       }
717       break;
718   }
719 }
720
721 /* add handlers, stuff you only do once or on area/region changes */
722 static void action_buttons_area_init(wmWindowManager *wm, ARegion *ar)
723 {
724   wmKeyMap *keymap;
725
726   ED_region_panels_init(wm, ar);
727
728   keymap = WM_keymap_ensure(wm->defaultconf, "Dopesheet Generic", SPACE_ACTION, 0);
729   WM_event_add_keymap_handler(&ar->handlers, keymap);
730 }
731
732 static void action_buttons_area_draw(const bContext *C, ARegion *ar)
733 {
734   ED_region_panels(C, ar);
735 }
736
737 static void action_region_listener(wmWindow *UNUSED(win),
738                                    ScrArea *UNUSED(sa),
739                                    ARegion *ar,
740                                    wmNotifier *wmn,
741                                    const Scene *UNUSED(scene))
742 {
743   /* context changes */
744   switch (wmn->category) {
745     case NC_ANIMATION:
746       ED_region_tag_redraw(ar);
747       break;
748     case NC_SCENE:
749       switch (wmn->data) {
750         case ND_OB_ACTIVE:
751         case ND_FRAME:
752         case ND_MARKERS:
753           ED_region_tag_redraw(ar);
754           break;
755       }
756       break;
757     case NC_OBJECT:
758       switch (wmn->data) {
759         case ND_BONE_ACTIVE:
760         case ND_BONE_SELECT:
761         case ND_KEYS:
762           ED_region_tag_redraw(ar);
763           break;
764       }
765       break;
766     default:
767       if (wmn->data == ND_KEYS) {
768         ED_region_tag_redraw(ar);
769       }
770       break;
771   }
772 }
773
774 static void action_refresh(const bContext *C, ScrArea *sa)
775 {
776   SpaceAction *saction = (SpaceAction *)sa->spacedata.first;
777
778   /* Update the state of the animchannels in response to changes from the data they represent
779    * NOTE: the temp flag is used to indicate when this needs to be done,
780    * and will be cleared once handled. */
781   if (saction->runtime.flag & SACTION_RUNTIME_FLAG_NEED_CHAN_SYNC) {
782     ARegion *ar;
783
784     /* Perform syncing of channel state incl. selection
785      * Active action setting also occurs here
786      * (as part of anim channel filtering in anim_filter.c). */
787     ANIM_sync_animchannels_to_data(C);
788     saction->runtime.flag &= ~SACTION_RUNTIME_FLAG_NEED_CHAN_SYNC;
789
790     /* Tag everything for redraw
791      * - Regions (such as header) need to be manually tagged for redraw too
792      *   or else they don't update [#28962]
793      */
794     ED_area_tag_redraw(sa);
795     for (ar = sa->regionbase.first; ar; ar = ar->next) {
796       ED_region_tag_redraw(ar);
797     }
798   }
799
800   /* region updates? */
801   // XXX re-sizing y-extents of tot should go here?
802 }
803
804 static void action_id_remap(ScrArea *UNUSED(sa), SpaceLink *slink, ID *old_id, ID *new_id)
805 {
806   SpaceAction *sact = (SpaceAction *)slink;
807
808   if ((ID *)sact->action == old_id) {
809     sact->action = (bAction *)new_id;
810   }
811
812   if ((ID *)sact->ads.filter_grp == old_id) {
813     sact->ads.filter_grp = (Collection *)new_id;
814   }
815   if ((ID *)sact->ads.source == old_id) {
816     sact->ads.source = new_id;
817   }
818 }
819
820 /**
821  * \note Used for splitting out a subset of modes is more involved,
822  * The previous non-timeline mode is stored so switching back to the
823  * dope-sheet doesn't always reset the sub-mode.
824  */
825 static int action_space_subtype_get(ScrArea *sa)
826 {
827   SpaceAction *sact = sa->spacedata.first;
828   return sact->mode == SACTCONT_TIMELINE ? SACTCONT_TIMELINE : SACTCONT_DOPESHEET;
829 }
830
831 static void action_space_subtype_set(ScrArea *sa, int value)
832 {
833   SpaceAction *sact = sa->spacedata.first;
834   if (value == SACTCONT_TIMELINE) {
835     if (sact->mode != SACTCONT_TIMELINE) {
836       sact->mode_prev = sact->mode;
837     }
838     sact->mode = value;
839   }
840   else {
841     sact->mode = sact->mode_prev;
842   }
843 }
844
845 static void action_space_subtype_item_extend(bContext *UNUSED(C),
846                                              EnumPropertyItem **item,
847                                              int *totitem)
848 {
849   RNA_enum_items_add(item, totitem, rna_enum_space_action_mode_items);
850 }
851
852 /* only called once, from space/spacetypes.c */
853 void ED_spacetype_action(void)
854 {
855   SpaceType *st = MEM_callocN(sizeof(SpaceType), "spacetype action");
856   ARegionType *art;
857
858   st->spaceid = SPACE_ACTION;
859   strncpy(st->name, "Action", BKE_ST_MAXNAME);
860
861   st->new = action_new;
862   st->free = action_free;
863   st->init = action_init;
864   st->duplicate = action_duplicate;
865   st->operatortypes = action_operatortypes;
866   st->keymap = action_keymap;
867   st->listener = action_listener;
868   st->refresh = action_refresh;
869   st->id_remap = action_id_remap;
870   st->space_subtype_item_extend = action_space_subtype_item_extend;
871   st->space_subtype_get = action_space_subtype_get;
872   st->space_subtype_set = action_space_subtype_set;
873
874   /* regions: main window */
875   art = MEM_callocN(sizeof(ARegionType), "spacetype action region");
876   art->regionid = RGN_TYPE_WINDOW;
877   art->init = action_main_region_init;
878   art->draw = action_main_region_draw;
879   art->listener = action_main_region_listener;
880   art->message_subscribe = saction_main_region_message_subscribe;
881   art->keymapflag = ED_KEYMAP_VIEW2D | ED_KEYMAP_MARKERS | ED_KEYMAP_ANIMATION | ED_KEYMAP_FRAMES;
882
883   BLI_addhead(&st->regiontypes, art);
884
885   /* regions: header */
886   art = MEM_callocN(sizeof(ARegionType), "spacetype action region");
887   art->regionid = RGN_TYPE_HEADER;
888   art->prefsizey = HEADERY;
889   art->keymapflag = ED_KEYMAP_UI | ED_KEYMAP_VIEW2D | ED_KEYMAP_FRAMES | ED_KEYMAP_HEADER;
890
891   art->init = action_header_region_init;
892   art->draw = action_header_region_draw;
893   art->listener = action_header_region_listener;
894
895   BLI_addhead(&st->regiontypes, art);
896
897   /* regions: channels */
898   art = MEM_callocN(sizeof(ARegionType), "spacetype action region");
899   art->regionid = RGN_TYPE_CHANNELS;
900   art->prefsizex = 200;
901   art->keymapflag = ED_KEYMAP_UI | ED_KEYMAP_VIEW2D | ED_KEYMAP_FRAMES;
902
903   art->init = action_channel_region_init;
904   art->draw = action_channel_region_draw;
905   art->listener = action_channel_region_listener;
906   art->message_subscribe = saction_channel_region_message_subscribe;
907
908   BLI_addhead(&st->regiontypes, art);
909
910   /* regions: UI buttons */
911   art = MEM_callocN(sizeof(ARegionType), "spacetype action region");
912   art->regionid = RGN_TYPE_UI;
913   art->prefsizex = 200;
914   art->keymapflag = ED_KEYMAP_UI;
915   art->listener = action_region_listener;
916   art->init = action_buttons_area_init;
917   art->draw = action_buttons_area_draw;
918
919   BLI_addhead(&st->regiontypes, art);
920
921   action_buttons_register(art);
922
923   BKE_spacetype_register(st);
924 }