8751fe5199d2b5236693b27e7ce3465e6c00d119
[blender.git] / source / blender / editors / space_graph / space_graph.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 spgraph
22  */
23
24 #include <string.h>
25 #include <stdio.h>
26
27 #include "DNA_anim_types.h"
28 #include "DNA_collection_types.h"
29 #include "DNA_scene_types.h"
30
31 #include "MEM_guardedalloc.h"
32
33 #include "BLI_blenlib.h"
34 #include "BLI_math.h"
35 #include "BLI_utildefines.h"
36
37 #include "BKE_context.h"
38 #include "BKE_fcurve.h"
39 #include "BKE_screen.h"
40
41 #include "ED_space_api.h"
42 #include "ED_screen.h"
43 #include "ED_anim_api.h"
44 #include "ED_markers.h"
45
46 #include "GPU_immediate.h"
47 #include "GPU_state.h"
48 #include "GPU_framebuffer.h"
49
50 #include "WM_api.h"
51 #include "WM_types.h"
52 #include "WM_message.h"
53
54 #include "RNA_access.h"
55 #include "RNA_define.h"
56 #include "RNA_enum_types.h"
57
58 #include "UI_resources.h"
59 #include "UI_view2d.h"
60
61 #include "graph_intern.h"  // own include
62
63 /* ******************** manage regions ********************* */
64
65 ARegion *graph_has_buttons_region(ScrArea *sa)
66 {
67   ARegion *ar, *arnew;
68
69   ar = BKE_area_find_region_type(sa, RGN_TYPE_UI);
70   if (ar)
71     return ar;
72
73   /* add subdiv level; after main */
74   ar = BKE_area_find_region_type(sa, RGN_TYPE_WINDOW);
75
76   /* is error! */
77   if (ar == NULL)
78     return NULL;
79
80   arnew = MEM_callocN(sizeof(ARegion), "buttons for graph");
81
82   BLI_insertlinkafter(&sa->regionbase, ar, arnew);
83   arnew->regiontype = RGN_TYPE_UI;
84   arnew->alignment = RGN_ALIGN_RIGHT;
85
86   arnew->flag = RGN_FLAG_HIDDEN;
87
88   return arnew;
89 }
90
91 /* ******************** default callbacks for ipo space ***************** */
92
93 static SpaceLink *graph_new(const ScrArea *UNUSED(sa), const Scene *scene)
94 {
95   ARegion *ar;
96   SpaceGraph *sipo;
97
98   /* Graph Editor - general stuff */
99   sipo = MEM_callocN(sizeof(SpaceGraph), "init graphedit");
100   sipo->spacetype = SPACE_GRAPH;
101
102   sipo->autosnap = SACTSNAP_FRAME;
103
104   /* allocate DopeSheet data for Graph Editor */
105   sipo->ads = MEM_callocN(sizeof(bDopeSheet), "GraphEdit DopeSheet");
106   sipo->ads->source = (ID *)scene;
107
108   /* settings for making it easier by default to just see what you're interested in tweaking */
109   sipo->ads->filterflag |= ADS_FILTER_ONLYSEL;
110   sipo->flag |= SIPO_SELVHANDLESONLY;
111
112   /* header */
113   ar = MEM_callocN(sizeof(ARegion), "header for graphedit");
114
115   BLI_addtail(&sipo->regionbase, ar);
116   ar->regiontype = RGN_TYPE_HEADER;
117   ar->alignment = (U.uiflag & USER_HEADER_BOTTOM) ? RGN_ALIGN_BOTTOM : RGN_ALIGN_TOP;
118
119   /* channels */
120   ar = MEM_callocN(sizeof(ARegion), "channels region for graphedit");
121
122   BLI_addtail(&sipo->regionbase, ar);
123   ar->regiontype = RGN_TYPE_CHANNELS;
124   ar->alignment = RGN_ALIGN_LEFT;
125
126   ar->v2d.scroll = (V2D_SCROLL_RIGHT | V2D_SCROLL_BOTTOM);
127
128   /* ui buttons */
129   ar = MEM_callocN(sizeof(ARegion), "buttons region for graphedit");
130
131   BLI_addtail(&sipo->regionbase, ar);
132   ar->regiontype = RGN_TYPE_UI;
133   ar->alignment = RGN_ALIGN_RIGHT;
134   ar->flag = RGN_FLAG_HIDDEN;
135
136   /* main region */
137   ar = MEM_callocN(sizeof(ARegion), "main region for graphedit");
138
139   BLI_addtail(&sipo->regionbase, ar);
140   ar->regiontype = RGN_TYPE_WINDOW;
141
142   ar->v2d.tot.xmin = 0.0f;
143   ar->v2d.tot.ymin = (float)scene->r.sfra - 10.0f;
144   ar->v2d.tot.xmax = (float)scene->r.efra;
145   ar->v2d.tot.ymax = 10.0f;
146
147   ar->v2d.cur = ar->v2d.tot;
148
149   ar->v2d.min[0] = FLT_MIN;
150   ar->v2d.min[1] = FLT_MIN;
151
152   ar->v2d.max[0] = MAXFRAMEF;
153   ar->v2d.max[1] = FLT_MAX;
154
155   ar->v2d.scroll = (V2D_SCROLL_BOTTOM | V2D_SCROLL_SCALE_HORIZONTAL);
156   ar->v2d.scroll |= (V2D_SCROLL_LEFT | V2D_SCROLL_SCALE_VERTICAL);
157
158   ar->v2d.keeptot = 0;
159
160   return (SpaceLink *)sipo;
161 }
162
163 /* not spacelink itself */
164 static void graph_free(SpaceLink *sl)
165 {
166   SpaceGraph *si = (SpaceGraph *)sl;
167
168   if (si->ads) {
169     BLI_freelistN(&si->ads->chanbase);
170     MEM_freeN(si->ads);
171   }
172
173   if (si->runtime.ghost_curves.first) {
174     free_fcurves(&si->runtime.ghost_curves);
175   }
176 }
177
178 /* spacetype; init callback */
179 static void graph_init(struct wmWindowManager *wm, ScrArea *sa)
180 {
181   SpaceGraph *sipo = (SpaceGraph *)sa->spacedata.first;
182
183   /* init dopesheet data if non-existent (i.e. for old files) */
184   if (sipo->ads == NULL) {
185     sipo->ads = MEM_callocN(sizeof(bDopeSheet), "GraphEdit DopeSheet");
186     sipo->ads->source = (ID *)WM_window_get_active_scene(wm->winactive);
187   }
188
189   /* force immediate init of any invalid F-Curve colors */
190   /* XXX: but, don't do SIPO_TEMP_NEEDCHANSYNC (i.e. channel select state sync)
191    * as this is run on each region resize; setting this here will cause selection
192    * state to be lost on area/region resizing. [#35744]
193    */
194   ED_area_tag_refresh(sa);
195 }
196
197 static SpaceLink *graph_duplicate(SpaceLink *sl)
198 {
199   SpaceGraph *sipon = MEM_dupallocN(sl);
200
201   /* clear or remove stuff from old */
202   BLI_duplicatelist(&sipon->runtime.ghost_curves, &((SpaceGraph *)sl)->runtime.ghost_curves);
203   sipon->ads = MEM_dupallocN(sipon->ads);
204
205   return (SpaceLink *)sipon;
206 }
207
208 /* add handlers, stuff you only do once or on area/region changes */
209 static void graph_main_region_init(wmWindowManager *wm, ARegion *ar)
210 {
211   wmKeyMap *keymap;
212
213   UI_view2d_region_reinit(&ar->v2d, V2D_COMMONVIEW_CUSTOM, ar->winx, ar->winy);
214
215   /* own keymap */
216   keymap = WM_keymap_ensure(wm->defaultconf, "Graph Editor", SPACE_GRAPH, 0);
217   WM_event_add_keymap_handler_bb(&ar->handlers, keymap, &ar->v2d.mask, &ar->winrct);
218   keymap = WM_keymap_ensure(wm->defaultconf, "Graph Editor Generic", SPACE_GRAPH, 0);
219   WM_event_add_keymap_handler(&ar->handlers, keymap);
220 }
221
222 static void graph_main_region_draw(const bContext *C, ARegion *ar)
223 {
224   /* draw entirely, view changes should be handled here */
225   SpaceGraph *sipo = CTX_wm_space_graph(C);
226   Scene *scene = CTX_data_scene(C);
227   bAnimContext ac;
228   View2D *v2d = &ar->v2d;
229   View2DGrid *grid;
230   View2DScrollers *scrollers;
231   float col[3];
232   short unitx = 0, unity = V2D_UNIT_VALUES, cfra_flag = 0;
233
234   /* clear and setup matrix */
235   UI_GetThemeColor3fv(TH_BACK, col);
236   GPU_clear_color(col[0], col[1], col[2], 0.0);
237   GPU_clear(GPU_COLOR_BIT);
238
239   UI_view2d_view_ortho(v2d);
240
241   /* grid */
242   unitx = ((sipo->mode == SIPO_MODE_ANIMATION) && (sipo->flag & SIPO_DRAWTIME)) ?
243               V2D_UNIT_SECONDS :
244               V2D_UNIT_FRAMESCALE;
245   grid = UI_view2d_grid_calc(CTX_data_scene(C),
246                              v2d,
247                              unitx,
248                              V2D_GRID_NOCLAMP,
249                              unity,
250                              V2D_GRID_NOCLAMP,
251                              ar->winx,
252                              ar->winy);
253   UI_view2d_grid_draw(v2d, grid, V2D_GRIDLINES_ALL);
254
255   ED_region_draw_cb_draw(C, ar, REGION_DRAW_PRE_VIEW);
256
257   /* start and end frame (in F-Curve mode only) */
258   if (sipo->mode != SIPO_MODE_DRIVERS) {
259     ANIM_draw_framerange(scene, v2d);
260   }
261
262   /* draw data */
263   if (ANIM_animdata_get_context(C, &ac)) {
264     /* draw ghost curves */
265     graph_draw_ghost_curves(&ac, sipo, ar);
266
267     /* draw curves twice - unselected, then selected, so that the are fewer occlusion problems */
268     graph_draw_curves(&ac, sipo, ar, grid, 0);
269     graph_draw_curves(&ac, sipo, ar, grid, 1);
270
271     /* XXX the slow way to set tot rect... but for nice sliders needed (ton) */
272     get_graph_keyframe_extents(
273         &ac, &v2d->tot.xmin, &v2d->tot.xmax, &v2d->tot.ymin, &v2d->tot.ymax, false, true);
274     /* extra offset so that these items are visible */
275     v2d->tot.xmin -= 10.0f;
276     v2d->tot.xmax += 10.0f;
277   }
278
279   /* only free grid after drawing data, as we need to use it to determine sampling rate */
280   UI_view2d_grid_free(grid);
281
282   if (((sipo->flag & SIPO_NODRAWCURSOR) == 0) || (sipo->mode == SIPO_MODE_DRIVERS)) {
283     uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
284
285     immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
286
287     /* horizontal component of value-cursor (value line before the current frame line) */
288     if ((sipo->flag & SIPO_NODRAWCURSOR) == 0) {
289       float y = sipo->cursorVal;
290
291       /* Draw a green line to indicate the cursor value */
292       immUniformThemeColorShadeAlpha(TH_CFRAME, -10, -50);
293       GPU_blend(true);
294       GPU_line_width(2.0);
295
296       immBegin(GPU_PRIM_LINES, 2);
297       immVertex2f(pos, v2d->cur.xmin, y);
298       immVertex2f(pos, v2d->cur.xmax, y);
299       immEnd();
300
301       GPU_blend(false);
302     }
303
304     /* current frame or vertical component of vertical component of the cursor */
305     if (sipo->mode == SIPO_MODE_DRIVERS) {
306       /* cursor x-value */
307       float x = sipo->cursorTime;
308
309       /* to help differentiate this from the current frame,
310        * draw slightly darker like the horizontal one */
311       immUniformThemeColorShadeAlpha(TH_CFRAME, -40, -50);
312       GPU_blend(true);
313       GPU_line_width(2.0);
314
315       immBegin(GPU_PRIM_LINES, 2);
316       immVertex2f(pos, x, v2d->cur.ymin);
317       immVertex2f(pos, x, v2d->cur.ymax);
318       immEnd();
319
320       GPU_blend(false);
321     }
322
323     immUnbindProgram();
324   }
325
326   if (sipo->mode != SIPO_MODE_DRIVERS) {
327     /* current frame */
328     if (sipo->flag & SIPO_DRAWTIME)
329       cfra_flag |= DRAWCFRA_UNIT_SECONDS;
330     ANIM_draw_cfra(C, v2d, cfra_flag);
331   }
332
333   /* markers */
334   UI_view2d_view_orthoSpecial(ar, v2d, 1);
335   int marker_draw_flag = DRAW_MARKERS_MARGIN;
336   if (sipo->flag & SIPO_MARKER_LINES) {
337     marker_draw_flag |= DRAW_MARKERS_LINES;
338   }
339   ED_markers_draw(C, marker_draw_flag);
340
341   /* preview range */
342   UI_view2d_view_ortho(v2d);
343   ANIM_draw_previewrange(C, v2d, 0);
344
345   /* callback */
346   UI_view2d_view_ortho(v2d);
347   ED_region_draw_cb_draw(C, ar, REGION_DRAW_POST_VIEW);
348
349   /* reset view matrix */
350   UI_view2d_view_restore(C);
351
352   /* scrollers */
353   // FIXME: args for scrollers depend on the type of data being shown...
354   scrollers = UI_view2d_scrollers_calc(
355       C, v2d, NULL, unitx, V2D_GRID_NOCLAMP, unity, V2D_GRID_NOCLAMP);
356   UI_view2d_scrollers_draw(C, v2d, scrollers);
357   UI_view2d_scrollers_free(scrollers);
358
359   /* draw current frame number-indicator on top of scrollers */
360   if ((sipo->mode != SIPO_MODE_DRIVERS) && ((sipo->flag & SIPO_NODRAWCFRANUM) == 0)) {
361     UI_view2d_view_orthoSpecial(ar, v2d, 1);
362     ANIM_draw_cfra_number(C, v2d, cfra_flag);
363   }
364 }
365
366 static void graph_channel_region_init(wmWindowManager *wm, ARegion *ar)
367 {
368   wmKeyMap *keymap;
369
370   /* make sure we keep the hide flags */
371   ar->v2d.scroll |= V2D_SCROLL_RIGHT;
372
373   /* prevent any noise of past */
374   ar->v2d.scroll &= ~(V2D_SCROLL_LEFT | V2D_SCROLL_TOP | V2D_SCROLL_BOTTOM);
375
376   ar->v2d.scroll |= V2D_SCROLL_HORIZONTAL_HIDE;
377   ar->v2d.scroll |= V2D_SCROLL_VERTICAL_HIDE;
378
379   UI_view2d_region_reinit(&ar->v2d, V2D_COMMONVIEW_LIST, ar->winx, ar->winy);
380
381   /* own keymap */
382   keymap = WM_keymap_ensure(wm->defaultconf, "Animation Channels", 0, 0);
383   WM_event_add_keymap_handler_bb(&ar->handlers, keymap, &ar->v2d.mask, &ar->winrct);
384   keymap = WM_keymap_ensure(wm->defaultconf, "Graph Editor Generic", SPACE_GRAPH, 0);
385   WM_event_add_keymap_handler(&ar->handlers, keymap);
386 }
387
388 static void graph_channel_region_draw(const bContext *C, ARegion *ar)
389 {
390   bAnimContext ac;
391   View2D *v2d = &ar->v2d;
392   View2DScrollers *scrollers;
393   float col[3];
394
395   /* clear and setup matrix */
396   UI_GetThemeColor3fv(TH_BACK, col);
397   GPU_clear_color(col[0], col[1], col[2], 0.0);
398   GPU_clear(GPU_COLOR_BIT);
399
400   UI_view2d_view_ortho(v2d);
401
402   /* draw channels */
403   if (ANIM_animdata_get_context(C, &ac)) {
404     graph_draw_channel_names((bContext *)C, &ac, ar);
405   }
406
407   /* reset view matrix */
408   UI_view2d_view_restore(C);
409
410   /* scrollers */
411   scrollers = UI_view2d_scrollers_calc(
412       C, v2d, NULL, V2D_ARG_DUMMY, V2D_ARG_DUMMY, V2D_ARG_DUMMY, V2D_ARG_DUMMY);
413   UI_view2d_scrollers_draw(C, v2d, scrollers);
414   UI_view2d_scrollers_free(scrollers);
415 }
416
417 /* add handlers, stuff you only do once or on area/region changes */
418 static void graph_header_region_init(wmWindowManager *UNUSED(wm), ARegion *ar)
419 {
420   ED_region_header_init(ar);
421 }
422
423 static void graph_header_region_draw(const bContext *C, ARegion *ar)
424 {
425   ED_region_header(C, ar);
426 }
427
428 /* add handlers, stuff you only do once or on area/region changes */
429 static void graph_buttons_region_init(wmWindowManager *wm, ARegion *ar)
430 {
431   wmKeyMap *keymap;
432
433   ED_region_panels_init(wm, ar);
434
435   keymap = WM_keymap_ensure(wm->defaultconf, "Graph Editor Generic", SPACE_GRAPH, 0);
436   WM_event_add_keymap_handler_bb(&ar->handlers, keymap, &ar->v2d.mask, &ar->winrct);
437 }
438
439 static void graph_buttons_region_draw(const bContext *C, ARegion *ar)
440 {
441   ED_region_panels(C, ar);
442 }
443
444 static void graph_region_listener(wmWindow *UNUSED(win),
445                                   ScrArea *UNUSED(sa),
446                                   ARegion *ar,
447                                   wmNotifier *wmn,
448                                   const Scene *UNUSED(scene))
449 {
450   /* context changes */
451   switch (wmn->category) {
452     case NC_ANIMATION:
453       ED_region_tag_redraw(ar);
454       break;
455     case NC_SCENE:
456       switch (wmn->data) {
457         case ND_RENDER_OPTIONS:
458         case ND_OB_ACTIVE:
459         case ND_FRAME:
460         case ND_FRAME_RANGE:
461         case ND_MARKERS:
462           ED_region_tag_redraw(ar);
463           break;
464         case ND_SEQUENCER:
465           if (wmn->action == NA_SELECTED)
466             ED_region_tag_redraw(ar);
467           break;
468       }
469       break;
470     case NC_OBJECT:
471       switch (wmn->data) {
472         case ND_BONE_ACTIVE:
473         case ND_BONE_SELECT:
474         case ND_KEYS:
475           ED_region_tag_redraw(ar);
476           break;
477         case ND_MODIFIER:
478           if (wmn->action == NA_RENAME)
479             ED_region_tag_redraw(ar);
480           break;
481       }
482       break;
483     case NC_NODE:
484       switch (wmn->action) {
485         case NA_EDITED:
486         case NA_SELECTED:
487           ED_region_tag_redraw(ar);
488           break;
489       }
490       break;
491     case NC_ID:
492       if (wmn->action == NA_RENAME)
493         ED_region_tag_redraw(ar);
494       break;
495     case NC_SCREEN:
496       if (ELEM(wmn->data, ND_LAYER)) {
497         ED_region_tag_redraw(ar);
498       }
499       break;
500     default:
501       if (wmn->data == ND_KEYS)
502         ED_region_tag_redraw(ar);
503       break;
504   }
505 }
506
507 static void graph_region_message_subscribe(const struct bContext *UNUSED(C),
508                                            struct WorkSpace *UNUSED(workspace),
509                                            struct Scene *scene,
510                                            struct bScreen *screen,
511                                            struct ScrArea *sa,
512                                            struct ARegion *ar,
513                                            struct wmMsgBus *mbus)
514 {
515   PointerRNA ptr;
516   RNA_pointer_create(&screen->id, &RNA_SpaceGraphEditor, sa->spacedata.first, &ptr);
517
518   wmMsgSubscribeValue msg_sub_value_region_tag_redraw = {
519       .owner = ar,
520       .user_data = ar,
521       .notify = ED_region_do_msg_notify_tag_redraw,
522   };
523
524   /* Timeline depends on scene properties. */
525   {
526     bool use_preview = (scene->r.flag & SCER_PRV_RANGE);
527     extern PropertyRNA rna_Scene_frame_start;
528     extern PropertyRNA rna_Scene_frame_end;
529     extern PropertyRNA rna_Scene_frame_preview_start;
530     extern PropertyRNA rna_Scene_frame_preview_end;
531     extern PropertyRNA rna_Scene_use_preview_range;
532     extern PropertyRNA rna_Scene_frame_current;
533     const PropertyRNA *props[] = {
534         use_preview ? &rna_Scene_frame_preview_start : &rna_Scene_frame_start,
535         use_preview ? &rna_Scene_frame_preview_end : &rna_Scene_frame_end,
536         &rna_Scene_use_preview_range,
537         &rna_Scene_frame_current,
538     };
539
540     PointerRNA idptr;
541     RNA_id_pointer_create(&scene->id, &idptr);
542
543     for (int i = 0; i < ARRAY_SIZE(props); i++) {
544       WM_msg_subscribe_rna(mbus, &idptr, props[i], &msg_sub_value_region_tag_redraw, __func__);
545     }
546   }
547
548   /* All dopesheet filter settings, etc. affect the drawing of this editor,
549    * also same applies for all animation-related datatypes that may appear here,
550    * so just whitelist the entire structs for updates
551    */
552   {
553     wmMsgParams_RNA msg_key_params = {{{0}}};
554     StructRNA *type_array[] = {
555         &RNA_DopeSheet, /* dopesheet filters */
556
557         &RNA_ActionGroup, /* channel groups */
558         &RNA_FCurve,      /* F-Curve */
559         &RNA_Keyframe,
560         &RNA_FCurveSample,
561
562         &RNA_FModifier, /* F-Modifiers (XXX: Why can't we just do all subclasses too?) */
563         &RNA_FModifierCycles,
564         &RNA_FModifierEnvelope,
565         &RNA_FModifierEnvelopeControlPoint,
566         &RNA_FModifierFunctionGenerator,
567         &RNA_FModifierGenerator,
568         &RNA_FModifierLimits,
569         &RNA_FModifierNoise,
570         &RNA_FModifierPython,
571         &RNA_FModifierStepped,
572     };
573
574     for (int i = 0; i < ARRAY_SIZE(type_array); i++) {
575       msg_key_params.ptr.type = type_array[i];
576       WM_msg_subscribe_rna_params(
577           mbus, &msg_key_params, &msg_sub_value_region_tag_redraw, __func__);
578     }
579   }
580 }
581
582 /* editor level listener */
583 static void graph_listener(wmWindow *UNUSED(win),
584                            ScrArea *sa,
585                            wmNotifier *wmn,
586                            Scene *UNUSED(scene))
587 {
588   SpaceGraph *sipo = (SpaceGraph *)sa->spacedata.first;
589
590   /* context changes */
591   switch (wmn->category) {
592     case NC_ANIMATION:
593       /* for selection changes of animation data, we can just redraw...
594        * otherwise autocolor might need to be done again */
595       if (ELEM(wmn->data, ND_KEYFRAME, ND_ANIMCHAN) && (wmn->action == NA_SELECTED))
596         ED_area_tag_redraw(sa);
597       else
598         ED_area_tag_refresh(sa);
599       break;
600     case NC_SCENE:
601       switch (wmn->data) {
602         case ND_OB_ACTIVE: /* Selection changed, so force refresh to flush
603                             * (needs flag set to do syncing). */
604         case ND_OB_SELECT:
605           sipo->runtime.flag |= SIPO_RUNTIME_FLAG_NEED_CHAN_SYNC;
606           ED_area_tag_refresh(sa);
607           break;
608
609         default: /* just redrawing the view will do */
610           ED_area_tag_redraw(sa);
611           break;
612       }
613       break;
614     case NC_OBJECT:
615       switch (wmn->data) {
616         case ND_BONE_SELECT: /* Selection changed, so force refresh to flush
617                               * (needs flag set to do syncing). */
618         case ND_BONE_ACTIVE:
619           sipo->runtime.flag |= SIPO_RUNTIME_FLAG_NEED_CHAN_SYNC;
620           ED_area_tag_refresh(sa);
621           break;
622         case ND_TRANSFORM:
623           break; /*do nothing*/
624
625         default: /* just redrawing the view will do */
626           ED_area_tag_redraw(sa);
627           break;
628       }
629       break;
630     case NC_NODE:
631       if (wmn->action == NA_SELECTED) {
632         /* selection changed, so force refresh to flush (needs flag set to do syncing) */
633         sipo->runtime.flag |= SIPO_RUNTIME_FLAG_NEED_CHAN_SYNC;
634         ED_area_tag_refresh(sa);
635       }
636       break;
637     case NC_SPACE:
638       if (wmn->data == ND_SPACE_GRAPH)
639         ED_area_tag_redraw(sa);
640       break;
641     case NC_WINDOW:
642       if (sipo->runtime.flag &
643           (SIPO_RUNTIME_FLAG_NEED_CHAN_SYNC | SIPO_RUNTIME_FLAG_NEED_CHAN_SYNC_COLOR)) {
644         /* force redraw/refresh after undo/redo - prevents "black curve" problem */
645         ED_area_tag_refresh(sa);
646       }
647       break;
648
649       // XXX: restore the case below if not enough updates occur...
650       //default:
651       //  if (wmn->data == ND_KEYS)
652       //      ED_area_tag_redraw(sa);
653   }
654 }
655
656 /* Update F-Curve colors */
657 static void graph_refresh_fcurve_colors(const bContext *C)
658 {
659   bAnimContext ac;
660
661   ListBase anim_data = {NULL, NULL};
662   bAnimListElem *ale;
663   size_t items;
664   int filter;
665   int i;
666
667   if (ANIM_animdata_get_context(C, &ac) == false)
668     return;
669
670   UI_SetTheme(SPACE_GRAPH, RGN_TYPE_WINDOW);
671
672   /* build list of F-Curves which will be visible as channels in channel-region
673    * - we don't include ANIMFILTER_CURVEVISIBLE filter, as that will result in a
674    *   mismatch between channel-colors and the drawn curves
675    */
676   filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_NODUPLIS);
677   items = ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
678
679   /* loop over F-Curves, assigning colors */
680   for (ale = anim_data.first, i = 0; ale; ale = ale->next, i++) {
681     FCurve *fcu = (FCurve *)ale->data;
682
683     /* set color of curve here */
684     switch (fcu->color_mode) {
685       case FCURVE_COLOR_CUSTOM: {
686         /* User has defined a custom color for this curve already (we assume it's not going to cause clashes with text colors),
687          * which should be left alone... Nothing needs to be done here.
688          */
689         break;
690       }
691       case FCURVE_COLOR_AUTO_RGB: {
692         /* F-Curve's array index is automatically mapped to RGB values. This works best of 3-value vectors.
693          * TODO: find a way to module the hue so that not all curves have same color...
694          */
695         float *col = fcu->color;
696
697         switch (fcu->array_index) {
698           case 0:
699             UI_GetThemeColor3fv(TH_AXIS_X, col);
700             break;
701           case 1:
702             UI_GetThemeColor3fv(TH_AXIS_Y, col);
703             break;
704           case 2:
705             UI_GetThemeColor3fv(TH_AXIS_Z, col);
706             break;
707           default:
708             /* 'unknown' color - bluish so as to not conflict with handles */
709             col[0] = 0.3f;
710             col[1] = 0.8f;
711             col[2] = 1.0f;
712             break;
713         }
714         break;
715       }
716       case FCURVE_COLOR_AUTO_YRGB: {
717         /* Like FCURVE_COLOR_AUTO_RGB, except this is for quaternions... */
718         float *col = fcu->color;
719
720         switch (fcu->array_index) {
721           case 1:
722             UI_GetThemeColor3fv(TH_AXIS_X, col);
723             break;
724           case 2:
725             UI_GetThemeColor3fv(TH_AXIS_Y, col);
726             break;
727           case 3:
728             UI_GetThemeColor3fv(TH_AXIS_Z, col);
729             break;
730
731           case 0: {
732             /* Special Case: "W" channel should be yellowish, so blend X and Y channel colors... */
733             float c1[3], c2[3];
734             float h1[3], h2[3];
735             float hresult[3];
736
737             /* - get colors (rgb) */
738             UI_GetThemeColor3fv(TH_AXIS_X, c1);
739             UI_GetThemeColor3fv(TH_AXIS_Y, c2);
740
741             /* - perform blending in HSV space (to keep brightness similar) */
742             rgb_to_hsv_v(c1, h1);
743             rgb_to_hsv_v(c2, h2);
744
745             interp_v3_v3v3(hresult, h1, h2, 0.5f);
746
747             /* - convert back to RGB for display */
748             hsv_to_rgb_v(hresult, col);
749             break;
750           }
751
752           default:
753             /* 'unknown' color - bluish so as to not conflict with handles */
754             col[0] = 0.3f;
755             col[1] = 0.8f;
756             col[2] = 1.0f;
757             break;
758         }
759         break;
760       }
761       case FCURVE_COLOR_AUTO_RAINBOW:
762       default: {
763         /* determine color 'automatically' using 'magic function' which uses the given args
764          * of current item index + total items to determine some RGB color
765          */
766         getcolor_fcurve_rainbow(i, items, fcu->color);
767         break;
768       }
769     }
770   }
771
772   /* free temp list */
773   ANIM_animdata_freelist(&anim_data);
774 }
775
776 static void graph_refresh(const bContext *C, ScrArea *sa)
777 {
778   SpaceGraph *sipo = (SpaceGraph *)sa->spacedata.first;
779
780   /* updates to data needed depends on Graph Editor mode... */
781   switch (sipo->mode) {
782     case SIPO_MODE_ANIMATION: /* all animation */
783     {
784       break;
785     }
786
787     case SIPO_MODE_DRIVERS: /* drivers only  */
788     {
789       break;
790     }
791   }
792
793   /* region updates? */
794   // XXX re-sizing y-extents of tot should go here?
795
796   /* update the state of the animchannels in response to changes from the data they represent
797    * NOTE: the temp flag is used to indicate when this needs to be done, and will be cleared once handled
798    */
799   if (sipo->runtime.flag & SIPO_RUNTIME_FLAG_NEED_CHAN_SYNC) {
800     ANIM_sync_animchannels_to_data(C);
801     sipo->runtime.flag &= ~SIPO_RUNTIME_FLAG_NEED_CHAN_SYNC;
802     ED_area_tag_redraw(sa);
803   }
804
805   /* We could check 'SIPO_RUNTIME_FLAG_NEED_CHAN_SYNC_COLOR', but color is recalculated anyway. */
806   if (sipo->runtime.flag & SIPO_RUNTIME_FLAG_NEED_CHAN_SYNC_COLOR) {
807     sipo->runtime.flag &= ~SIPO_RUNTIME_FLAG_NEED_CHAN_SYNC_COLOR;
808 #if 0 /* Done below. */
809     graph_refresh_fcurve_colors(C);
810 #endif
811     ED_area_tag_redraw(sa);
812   }
813
814   /* init/adjust F-Curve colors */
815   graph_refresh_fcurve_colors(C);
816 }
817
818 static void graph_id_remap(ScrArea *UNUSED(sa), SpaceLink *slink, ID *old_id, ID *new_id)
819 {
820   SpaceGraph *sgraph = (SpaceGraph *)slink;
821
822   if (sgraph->ads) {
823     if ((ID *)sgraph->ads->filter_grp == old_id) {
824       sgraph->ads->filter_grp = (Collection *)new_id;
825     }
826     if ((ID *)sgraph->ads->source == old_id) {
827       sgraph->ads->source = new_id;
828     }
829   }
830 }
831
832 static int graph_space_subtype_get(ScrArea *sa)
833 {
834   SpaceGraph *sgraph = sa->spacedata.first;
835   return sgraph->mode;
836 }
837
838 static void graph_space_subtype_set(ScrArea *sa, int value)
839 {
840   SpaceGraph *sgraph = sa->spacedata.first;
841   sgraph->mode = value;
842 }
843
844 static void graph_space_subtype_item_extend(bContext *UNUSED(C),
845                                             EnumPropertyItem **item,
846                                             int *totitem)
847 {
848   RNA_enum_items_add(item, totitem, rna_enum_space_graph_mode_items);
849 }
850
851 /* only called once, from space/spacetypes.c */
852 void ED_spacetype_ipo(void)
853 {
854   SpaceType *st = MEM_callocN(sizeof(SpaceType), "spacetype ipo");
855   ARegionType *art;
856
857   st->spaceid = SPACE_GRAPH;
858   strncpy(st->name, "Graph", BKE_ST_MAXNAME);
859
860   st->new = graph_new;
861   st->free = graph_free;
862   st->init = graph_init;
863   st->duplicate = graph_duplicate;
864   st->operatortypes = graphedit_operatortypes;
865   st->keymap = graphedit_keymap;
866   st->listener = graph_listener;
867   st->refresh = graph_refresh;
868   st->id_remap = graph_id_remap;
869   st->space_subtype_item_extend = graph_space_subtype_item_extend;
870   st->space_subtype_get = graph_space_subtype_get;
871   st->space_subtype_set = graph_space_subtype_set;
872
873   /* regions: main window */
874   art = MEM_callocN(sizeof(ARegionType), "spacetype graphedit region");
875   art->regionid = RGN_TYPE_WINDOW;
876   art->init = graph_main_region_init;
877   art->draw = graph_main_region_draw;
878   art->listener = graph_region_listener;
879   art->message_subscribe = graph_region_message_subscribe;
880   art->keymapflag = ED_KEYMAP_VIEW2D | ED_KEYMAP_MARKERS | ED_KEYMAP_ANIMATION | ED_KEYMAP_FRAMES;
881
882   BLI_addhead(&st->regiontypes, art);
883
884   /* regions: header */
885   art = MEM_callocN(sizeof(ARegionType), "spacetype graphedit region");
886   art->regionid = RGN_TYPE_HEADER;
887   art->prefsizey = HEADERY;
888   art->keymapflag = ED_KEYMAP_UI | ED_KEYMAP_VIEW2D | ED_KEYMAP_FRAMES | ED_KEYMAP_HEADER;
889   art->listener = graph_region_listener;
890   art->init = graph_header_region_init;
891   art->draw = graph_header_region_draw;
892
893   BLI_addhead(&st->regiontypes, art);
894
895   /* regions: channels */
896   art = MEM_callocN(sizeof(ARegionType), "spacetype graphedit region");
897   art->regionid = RGN_TYPE_CHANNELS;
898   /* 200 is the 'standard', but due to scrollers, we want a bit more to fit the lock icons in */
899   art->prefsizex = 200 + V2D_SCROLL_WIDTH;
900   art->keymapflag = ED_KEYMAP_UI | ED_KEYMAP_VIEW2D | ED_KEYMAP_FRAMES;
901   art->listener = graph_region_listener;
902   art->message_subscribe = graph_region_message_subscribe;
903   art->init = graph_channel_region_init;
904   art->draw = graph_channel_region_draw;
905
906   BLI_addhead(&st->regiontypes, art);
907
908   /* regions: UI buttons */
909   art = MEM_callocN(sizeof(ARegionType), "spacetype graphedit region");
910   art->regionid = RGN_TYPE_UI;
911   art->prefsizex = 200;
912   art->keymapflag = ED_KEYMAP_UI | ED_KEYMAP_FRAMES;
913   art->listener = graph_region_listener;
914   art->init = graph_buttons_region_init;
915   art->draw = graph_buttons_region_draw;
916
917   BLI_addhead(&st->regiontypes, art);
918
919   graph_buttons_register(art);
920
921   BKE_spacetype_register(st);
922 }