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