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