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