Workbench: Cleaner Shadow edges own shadow
[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_BOTTOM;
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
530 /* editor level listener */
531 static void graph_listener(bScreen *UNUSED(sc), ScrArea *sa, wmNotifier *wmn, Scene *UNUSED(scene),
532                            WorkSpace *UNUSED(workspace))
533 {
534         SpaceIpo *sipo = (SpaceIpo *)sa->spacedata.first;
535         
536         /* context changes */
537         switch (wmn->category) {
538                 case NC_ANIMATION:
539                         /* for selection changes of animation data, we can just redraw... otherwise autocolor might need to be done again */
540                         if (ELEM(wmn->data, ND_KEYFRAME, ND_ANIMCHAN) && (wmn->action == NA_SELECTED))
541                                 ED_area_tag_redraw(sa);
542                         else
543                                 ED_area_tag_refresh(sa);
544                         break;
545                 case NC_SCENE:
546                         switch (wmn->data) {
547                                 case ND_OB_ACTIVE:  /* selection changed, so force refresh to flush (needs flag set to do syncing)  */
548                                 case ND_OB_SELECT:
549                                         sipo->flag |= SIPO_TEMP_NEEDCHANSYNC;
550                                         ED_area_tag_refresh(sa);
551                                         break;
552                                         
553                                 default: /* just redrawing the view will do */
554                                         ED_area_tag_redraw(sa);
555                                         break;
556                         }
557                         break;
558                 case NC_OBJECT:
559                         switch (wmn->data) {
560                                 case ND_BONE_SELECT:    /* selection changed, so force refresh to flush (needs flag set to do syncing) */
561                                 case ND_BONE_ACTIVE:
562                                         sipo->flag |= SIPO_TEMP_NEEDCHANSYNC;
563                                         ED_area_tag_refresh(sa);
564                                         break;
565                                 case ND_TRANSFORM:
566                                         break; /*do nothing*/
567                                         
568                                 default: /* just redrawing the view will do */
569                                         ED_area_tag_redraw(sa);
570                                         break;
571                         }
572                         break;
573                 case NC_NODE:
574                         if (wmn->action == NA_SELECTED) {
575                                 /* selection changed, so force refresh to flush (needs flag set to do syncing) */
576                                 sipo->flag |= SIPO_TEMP_NEEDCHANSYNC;
577                                 ED_area_tag_refresh(sa);
578                         }
579                         break;
580                 case NC_SPACE:
581                         if (wmn->data == ND_SPACE_GRAPH)
582                                 ED_area_tag_redraw(sa);
583                         break;
584                 case NC_WINDOW:
585                         if (sipo->flag & SIPO_TEMP_NEEDCHANSYNC) {
586                                 /* force redraw/refresh after undo/redo - prevents "black curve" problem */
587                                 ED_area_tag_refresh(sa);
588                         }
589                         break;
590                         
591                         // XXX: restore the case below if not enough updates occur...
592                         //default:
593                         //      if (wmn->data == ND_KEYS)
594                         //              ED_area_tag_redraw(sa);
595         }
596 }
597
598 /* Update F-Curve colors */
599 static void graph_refresh_fcurve_colors(const bContext *C)
600 {
601         bAnimContext ac;
602         
603         ListBase anim_data = {NULL, NULL};
604         bAnimListElem *ale;
605         size_t items;
606         int filter;
607         int i;
608         
609         if (ANIM_animdata_get_context(C, &ac) == false)
610                 return;
611         
612         UI_SetTheme(SPACE_IPO, RGN_TYPE_WINDOW);
613         
614         /* build list of F-Curves which will be visible as channels in channel-region
615          *  - we don't include ANIMFILTER_CURVEVISIBLE filter, as that will result in a
616          *    mismatch between channel-colors and the drawn curves
617          */
618         filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_NODUPLIS);
619         items = ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
620         
621         /* loop over F-Curves, assigning colors */
622         for (ale = anim_data.first, i = 0; ale; ale = ale->next, i++) {
623                 FCurve *fcu = (FCurve *)ale->data;
624                 
625                 /* set color of curve here */
626                 switch (fcu->color_mode) {
627                         case FCURVE_COLOR_CUSTOM:
628                         {
629                                 /* User has defined a custom color for this curve already (we assume it's not going to cause clashes with text colors),
630                                  * which should be left alone... Nothing needs to be done here.
631                                  */
632                                 break;
633                         }
634                         case FCURVE_COLOR_AUTO_RGB:
635                         {
636                                 /* F-Curve's array index is automatically mapped to RGB values. This works best of 3-value vectors. 
637                                  * TODO: find a way to module the hue so that not all curves have same color...
638                                  */
639                                 float *col = fcu->color;
640                                 
641                                 switch (fcu->array_index) {
642                                         case 0:
643                                                 UI_GetThemeColor3fv(TH_AXIS_X, col);
644                                                 break;
645                                         case 1:
646                                                 UI_GetThemeColor3fv(TH_AXIS_Y, col);
647                                                 break;
648                                         case 2:
649                                                 UI_GetThemeColor3fv(TH_AXIS_Z, col);
650                                                 break;
651                                         default:
652                                                 /* 'unknown' color - bluish so as to not conflict with handles */
653                                                 col[0] = 0.3f; col[1] = 0.8f; col[2] = 1.0f;
654                                                 break;
655                                 }
656                                 break;
657                         }
658                         case FCURVE_COLOR_AUTO_YRGB:
659                         {
660                                 /* Like FCURVE_COLOR_AUTO_RGB, except this is for quaternions... */
661                                 float *col = fcu->color;
662                                 
663                                 switch (fcu->array_index) {
664                                         case 1:
665                                                 UI_GetThemeColor3fv(TH_AXIS_X, col);
666                                                 break;
667                                         case 2:
668                                                 UI_GetThemeColor3fv(TH_AXIS_Y, col);
669                                                 break;
670                                         case 3:
671                                                 UI_GetThemeColor3fv(TH_AXIS_Z, col);
672                                                 break;
673                                         
674                                         case 0:
675                                         {
676                                                 /* Special Case: "W" channel should be yellowish, so blend X and Y channel colors... */
677                                                 float c1[3], c2[3];
678                                                 float h1[3], h2[3];
679                                                 float hresult[3];
680                                                 
681                                                 /* - get colors (rgb) */
682                                                 UI_GetThemeColor3fv(TH_AXIS_X, c1);
683                                                 UI_GetThemeColor3fv(TH_AXIS_Y, c2);
684                                                 
685                                                 /* - perform blending in HSV space (to keep brightness similar) */
686                                                 rgb_to_hsv_v(c1, h1);
687                                                 rgb_to_hsv_v(c2, h2);
688                                                 
689                                                 interp_v3_v3v3(hresult, h1, h2, 0.5f);
690                                                 
691                                                 /* - convert back to RGB for display */
692                                                 hsv_to_rgb_v(hresult, col);
693                                                 break;
694                                         }
695                                         
696                                         default:
697                                                 /* 'unknown' color - bluish so as to not conflict with handles */
698                                                 col[0] = 0.3f; col[1] = 0.8f; col[2] = 1.0f;
699                                                 break;
700                                 }
701                                 break;
702                         }
703                         case FCURVE_COLOR_AUTO_RAINBOW:
704                         default:
705                         {
706                                 /* determine color 'automatically' using 'magic function' which uses the given args
707                                  * of current item index + total items to determine some RGB color
708                                  */
709                                 getcolor_fcurve_rainbow(i, items, fcu->color);
710                                 break;
711                         }
712                 }
713         }
714         
715         /* free temp list */
716         ANIM_animdata_freelist(&anim_data);
717 }
718
719 static void graph_refresh(const bContext *C, ScrArea *sa)
720 {
721         SpaceIpo *sipo = (SpaceIpo *)sa->spacedata.first;
722         
723         /* updates to data needed depends on Graph Editor mode... */
724         switch (sipo->mode) {
725                 case SIPO_MODE_ANIMATION: /* all animation */
726                 {
727                         break;
728                 }
729                 
730                 case SIPO_MODE_DRIVERS: /* drivers only  */
731                 {
732                         break;
733                 }
734         }
735         
736         /* region updates? */
737         // XXX re-sizing y-extents of tot should go here?
738         
739         /* update the state of the animchannels in response to changes from the data they represent 
740          * NOTE: the temp flag is used to indicate when this needs to be done, and will be cleared once handled
741          */
742         if (sipo->flag & SIPO_TEMP_NEEDCHANSYNC) {
743                 ANIM_sync_animchannels_to_data(C);
744                 sipo->flag &= ~SIPO_TEMP_NEEDCHANSYNC;
745                 ED_area_tag_redraw(sa);
746         }
747         
748         /* init/adjust F-Curve colors */
749         graph_refresh_fcurve_colors(C);
750 }
751
752 static void graph_id_remap(ScrArea *UNUSED(sa), SpaceLink *slink, ID *old_id, ID *new_id)
753 {
754         SpaceIpo *sgraph = (SpaceIpo *)slink;
755         
756         if (sgraph->ads) {
757                 if ((ID *)sgraph->ads->filter_grp == old_id) {
758                         sgraph->ads->filter_grp = (Group *)new_id;
759                 }
760                 if ((ID *)sgraph->ads->source == old_id) {
761                         sgraph->ads->source = new_id;
762                 }
763         }
764 }
765
766 /* only called once, from space/spacetypes.c */
767 void ED_spacetype_ipo(void)
768 {
769         SpaceType *st = MEM_callocN(sizeof(SpaceType), "spacetype ipo");
770         ARegionType *art;
771         
772         st->spaceid = SPACE_IPO;
773         strncpy(st->name, "Graph", BKE_ST_MAXNAME);
774         
775         st->new = graph_new;
776         st->free = graph_free;
777         st->init = graph_init;
778         st->duplicate = graph_duplicate;
779         st->operatortypes = graphedit_operatortypes;
780         st->keymap = graphedit_keymap;
781         st->listener = graph_listener;
782         st->refresh = graph_refresh;
783         st->id_remap = graph_id_remap;
784
785         /* regions: main window */
786         art = MEM_callocN(sizeof(ARegionType), "spacetype graphedit region");
787         art->regionid = RGN_TYPE_WINDOW;
788         art->init = graph_main_region_init;
789         art->draw = graph_main_region_draw;
790         art->listener = graph_region_listener;
791         art->message_subscribe = graph_region_message_subscribe;
792         art->keymapflag = ED_KEYMAP_VIEW2D | ED_KEYMAP_MARKERS | ED_KEYMAP_ANIMATION | ED_KEYMAP_FRAMES;
793
794         BLI_addhead(&st->regiontypes, art);
795         
796         /* regions: header */
797         art = MEM_callocN(sizeof(ARegionType), "spacetype graphedit region");
798         art->regionid = RGN_TYPE_HEADER;
799         art->prefsizey = HEADERY;
800         art->keymapflag = ED_KEYMAP_UI | ED_KEYMAP_VIEW2D | ED_KEYMAP_FRAMES | ED_KEYMAP_HEADER;
801         art->listener = graph_region_listener;
802         art->init = graph_header_region_init;
803         art->draw = graph_header_region_draw;
804         
805         BLI_addhead(&st->regiontypes, art);
806         
807         /* regions: channels */
808         art = MEM_callocN(sizeof(ARegionType), "spacetype graphedit region");
809         art->regionid = RGN_TYPE_CHANNELS;
810         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 */
811         art->keymapflag = ED_KEYMAP_UI | ED_KEYMAP_VIEW2D | ED_KEYMAP_FRAMES;
812         art->listener = graph_region_listener;
813         art->init = graph_channel_region_init;
814         art->draw = graph_channel_region_draw;
815         
816         BLI_addhead(&st->regiontypes, art);
817         
818         /* regions: UI buttons */
819         art = MEM_callocN(sizeof(ARegionType), "spacetype graphedit region");
820         art->regionid = RGN_TYPE_UI;
821         art->prefsizex = 200;
822         art->keymapflag = ED_KEYMAP_UI | ED_KEYMAP_FRAMES;
823         art->listener = graph_region_listener;
824         art->init = graph_buttons_region_init;
825         art->draw = graph_buttons_region_draw;
826         
827         BLI_addhead(&st->regiontypes, art);
828
829         graph_buttons_register(art);
830         
831         BKE_spacetype_register(st);
832 }
833