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