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