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