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