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