UI: various panels fixes.
[blender.git] / source / blender / editors / space_graph / graph_buttons.c
1 /**
2  * $Id:
3  *
4  * ***** BEGIN GPL LICENSE BLOCK *****
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version. 
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
19  *
20  * The Original Code is Copyright (C) 2009 Blender Foundation.
21  * All rights reserved.
22  *
23  * 
24  * Contributor(s): Blender Foundation, Joshua Leung
25  *
26  * ***** END GPL LICENSE BLOCK *****
27  */
28
29 #include <string.h>
30 #include <stdio.h>
31 #include <math.h>
32 #include <float.h>
33
34 #include "DNA_anim_types.h"
35 #include "DNA_action_types.h"
36 #include "DNA_object_types.h"
37 #include "DNA_space_types.h"
38 #include "DNA_scene_types.h"
39 #include "DNA_screen_types.h"
40 #include "DNA_userdef_types.h"
41
42 #include "MEM_guardedalloc.h"
43
44 #include "BLI_arithb.h"
45 #include "BLI_blenlib.h"
46 #include "BLI_editVert.h"
47 #include "BLI_rand.h"
48
49 #include "BKE_animsys.h"
50 #include "BKE_action.h"
51 #include "BKE_context.h"
52 #include "BKE_curve.h"
53 #include "BKE_customdata.h"
54 #include "BKE_depsgraph.h"
55 #include "BKE_fcurve.h"
56 #include "BKE_object.h"
57 #include "BKE_global.h"
58 #include "BKE_scene.h"
59 #include "BKE_screen.h"
60 #include "BKE_utildefines.h"
61
62 #include "BIF_gl.h"
63
64 #include "WM_api.h"
65 #include "WM_types.h"
66
67 #include "RNA_access.h"
68 #include "RNA_define.h"
69
70 #include "ED_anim_api.h"
71 #include "ED_keyframing.h"
72 #include "ED_screen.h"
73 #include "ED_types.h"
74 #include "ED_util.h"
75
76 #include "UI_interface.h"
77 #include "UI_resources.h"
78 #include "UI_view2d.h"
79
80 #include "graph_intern.h"       // own include
81
82
83 /* ******************* graph editor space & buttons ************** */
84
85 #define B_NOP           1
86 #define B_REDR          2
87
88 /* -------------- */
89
90 static void do_graph_region_buttons(bContext *C, void *arg, int event)
91 {
92         //Scene *scene= CTX_data_scene(C);
93         
94         switch(event) {
95
96         }
97         
98         /* default for now */
99         //WM_event_add_notifier(C, NC_OBJECT|ND_TRANSFORM, ob);
100 }
101
102 static void graph_panel_properties(const bContext *C, ARegion *ar, short cntrl, bAnimListElem *ale)     
103 {
104         FCurve *fcu= (FCurve *)ale->data;
105         uiBlock *block;
106         char name[128];
107
108         block= uiBeginBlock(C, ar, "graph_panel_properties", UI_EMBOSS, UI_HELV);
109         if (uiNewPanel(C, ar, block, "Properties", "Graph", 340, 30, 318, 254)==0) return;
110         uiBlockSetHandleFunc(block, do_graph_region_buttons, NULL);
111
112         /* to force height */
113         uiNewPanelHeight(block, 204);
114
115         /* Info - Active F-Curve */
116         uiDefBut(block, LABEL, 1, "Active F-Curve:",                                    10, 200, 150, 19, NULL, 0.0, 0.0, 0, 0, "");
117         
118         if (ale->id) { 
119                 // icon of active blocktype - is this really necessary?
120                 int icon= geticon_anim_blocktype(GS(ale->id->name));
121                 
122                 // xxx type of icon-but is currently "LABEL", as that one is plain...
123                 uiDefIconBut(block, LABEL, 1, icon, 10, 180, 20, 19, NULL, 0, 0, 0, 0, "ID-type that F-Curve belongs to");
124         }
125         
126         getname_anim_fcurve(name, ale->id, fcu);
127         uiDefBut(block, LABEL, 1, name, 30, 180, 300, 19, NULL, 0.0, 0.0, 0, 0, "Name of Active F-Curve");
128         
129         /* TODO: the following settings could be added here
130          *      - F-Curve coloring mode - mode selector + color selector
131          *      - Access details (ID-block + RNA-Path + Array Index)
132          *      - ...
133          */
134 }
135
136 /* ******************* drivers ******************************** */
137
138 #define B_IPO_DEPCHANGE         10
139
140 static void do_graph_region_driver_buttons(bContext *C, void *arg, int event)
141 {
142         Scene *scene= CTX_data_scene(C);
143         
144         switch(event) {
145                 case B_IPO_DEPCHANGE:
146                 {
147                         /* rebuild depsgraph for the new deps */
148                         DAG_scene_sort(scene);
149                         
150                         /* TODO: which one? we need some way of sending these updates since curves from non-active ob could be being edited */
151                         //DAG_object_flush_update(G.scene, ob, OB_RECALC_DATA);
152                         //DAG_object_flush_update(scene, ob, OB_RECALC_OB);
153                 }
154                         break;
155         }
156         
157         /* default for now */
158         //WM_event_add_notifier(C, NC_OBJECT|ND_TRANSFORM, ob);
159 }
160
161 static void graph_panel_drivers(const bContext *C, ARegion *ar, short cntrl, bAnimListElem *ale)        
162 {
163         FCurve *fcu= (FCurve *)ale->data;
164         ChannelDriver *driver= fcu->driver;
165         uiBlock *block;
166         uiBut *but;
167         int len;
168
169         block= uiBeginBlock(C, ar, "graph_panel_drivers", UI_EMBOSS, UI_HELV);
170         if (uiNewPanel(C, ar, block, "Drivers", "Graph", 340, 30, 318, 254)==0) return;
171         uiBlockSetHandleFunc(block, do_graph_region_driver_buttons, NULL);
172
173         /* to force height */
174         uiNewPanelHeight(block, 204);
175         
176         /* type */
177         uiDefBut(block, LABEL, 1, "Type:",                                      10, 200, 120, 20, NULL, 0.0, 0.0, 0, 0, "");
178         uiDefButI(block, MENU, B_IPO_DEPCHANGE,
179                                         "Driver Type%t|Transform Channel%x0|Scripted Expression%x1|Rotational Difference%x2", 
180                                         130,200,180,20, &driver->type, 0, 0, 0, 0, "Driver type");
181                                         
182         /* buttons to draw depends on type of driver */
183         if (driver->type == DRIVER_TYPE_PYTHON) { /* PyDriver */
184                 uiDefBut(block, TEX, B_REDR, "Expr: ", 10,160,300,20, driver->expression, 0, 255, 0, 0, "One-liner Python Expression to use as Scripted Expression");
185                 
186                 if (driver->flag & DRIVER_FLAG_INVALID) {
187                         uiDefIconBut(block, LABEL, 1, ICON_ERROR, 10, 140, 20, 19, NULL, 0, 0, 0, 0, "");
188                         uiDefBut(block, LABEL, 0, "Error: invalid Python expression",
189                                         30,140,230,19, NULL, 0, 0, 0, 0, "");
190                 }
191         }
192         else { /* Channel or RotDiff - RotDiff just has extra settings */
193                 /* Driver Object */
194                 but= uiDefBut(block, TEX, B_IPO_DEPCHANGE, "OB: ",      10,160,150,20, driver->id->name+2, 0.0, 21.0, 0, 0, "Object that controls this Driver.");
195                 uiButSetFunc(but, test_idbutton_cb, driver->id->name, NULL);
196                 
197                 // XXX should we hide these technical details?
198                 if (driver->id) {
199                         /* Array Index */
200                         // XXX ideally this is grouped with the path, but that can get quite long...
201                         uiDefButI(block, NUM, B_IPO_DEPCHANGE, "Index: ", 170,160,140,20, &driver->array_index, 0, INT_MAX, 0, 0, "Index to the specific property used as Driver if applicable.");
202                         
203                         /* RNA-Path - allocate if non-existant */
204                         if (driver->rna_path == NULL) {
205                                 driver->rna_path= MEM_callocN(256, "Driver RNA-Path");
206                                 len= 255;
207                         }
208                         else
209                                 len= strlen(driver->rna_path);
210                         uiDefBut(block, TEX, B_IPO_DEPCHANGE, "Path: ", 10,130,300,20, driver->rna_path, 0, len, 0, 0, "RNA Path (from Driver Object) to property used as Driver.");
211                 }
212                 
213                 /* for rotational difference, show second target... */
214                 if (driver->type == DRIVER_TYPE_ROTDIFF) {
215                         // TODO...
216                 }
217         }
218 }
219
220 /* ******************* f-modifiers ******************************** */
221
222 #define B_FMODIFIER_REDRAW              20
223
224 static void do_graph_region_modifier_buttons(bContext *C, void *arg, int event)
225 {
226         switch (event) {
227                 case B_REDR:
228                 case B_FMODIFIER_REDRAW: // XXX this should send depsgraph updates too
229                         ED_area_tag_redraw(CTX_wm_area(C));
230                         return; /* no notifier! */
231         }
232 }
233
234 /* macro for use here to draw background box and set height */
235 // XXX for now, roundbox has it's callback func set to NULL to not intercept events
236 #define DRAW_BACKDROP(height) \
237         { \
238                 if (active) uiBlockSetCol(block, TH_BUT_ACTION); \
239                         but= uiDefBut(block, ROUNDBOX, B_REDR, "", 10-8, *yco-height, width, height-1, NULL, 5.0, 0.0, 12.0, (float)rb_col, ""); \
240                         uiButSetFunc(but, NULL, NULL, NULL); \
241                 if (active) uiBlockSetCol(block, TH_AUTO); \
242         }
243
244 /* callback to verify modifier data */
245 static void validate_fmodifier_cb (bContext *C, void *fcu_v, void *fcm_v)
246 {
247         FModifier *fcm= (FModifier *)fcm_v;
248         FModifierTypeInfo *fmi= fmodifier_get_typeinfo(fcm);
249         
250         /* call the verify callback on the modifier if applicable */
251         if (fmi && fmi->verify_data)
252                 fmi->verify_data(fcm);
253 }
254
255 /* callback to set the active modifier */
256 static void activate_fmodifier_cb (bContext *C, void *fcu_v, void *fcm_v)
257 {
258         FCurve *fcu= (FCurve *)fcu_v;
259         FModifier *fcm= (FModifier *)fcm_v;
260         
261         /* call API function to set the active modifier for active F-Curve */
262         fcurve_set_active_modifier(fcu, fcm);
263 }
264
265 /* callback to remove the given modifier  */
266 static void delete_fmodifier_cb (bContext *C, void *fcu_v, void *fcm_v)
267 {
268         FCurve *fcu= (FCurve *)fcu_v;
269         FModifier *fcm= (FModifier *)fcm_v;
270         
271         /* remove the given F-Modifier from the F-Curve */
272         fcurve_remove_modifier(fcu, fcm);
273 }
274
275 /* --------------- */
276         
277 /* draw settings for generator modifier */
278 static void draw_modifier__generator(uiBlock *block, FCurve *fcu, FModifier *fcm, int *yco, short *height, short width, short active, int rb_col)
279 {
280         FMod_Generator *data= (FMod_Generator *)fcm->data;
281         char gen_mode[]="Generator Type%t|Expanded Polynomial%x0|Factorised Polynomial%x1|Built-In Function%x2|Expression%x3";
282         char fn_type[]="Built-In Function%t|Sin%x0|Cos%x1|Tan%x2|Square Root%x3|Natural Log%x4";
283         int cy= *yco - 30;
284         uiBut *but;
285         
286         /* set the height */
287         (*height) = 90;
288         switch (data->mode) {
289                 case FCM_GENERATOR_POLYNOMIAL: /* polynomial expression */
290                         (*height) += 20*(data->poly_order+1) + 35;
291                         break;
292                 case FCM_GENERATOR_POLYNOMIAL_FACTORISED: /* factorised polynomial */
293                         (*height) += 20 * data->poly_order;
294                         break;
295                 case FCM_GENERATOR_FUNCTION: /* builtin function */
296                         (*height) += 50; // xxx
297                         break;
298                 case FCM_GENERATOR_EXPRESSION: /* py-expression */
299                         // xxx nothing to draw 
300                         break;
301         }
302         
303         /* basic settings (backdrop + mode selector + some padding) */
304         //DRAW_BACKDROP((*height)); // XXX buggy...
305         uiBlockBeginAlign(block);
306                 but= uiDefButS(block, MENU, B_FMODIFIER_REDRAW, gen_mode, 10,cy,width-30,19, &data->mode, 0, 0, 0, 0, "Selects type of generator algorithm.");
307                 uiButSetFunc(but, validate_fmodifier_cb, fcu, fcm);
308                 cy -= 20;
309                 
310                 uiDefButBitS(block, TOG, FCM_GENERATOR_ADDITIVE, B_FMODIFIER_REDRAW, "Additive", 10,cy,width-30,19, &data->flag, 0, 0, 0, 0, "Values generated by this modifier are applied on top of the existing values instead of overwriting them");
311                 cy -= 35;
312         uiBlockEndAlign(block);
313         
314         /* now add settings for individual modes */
315         switch (data->mode) {
316                 case FCM_GENERATOR_POLYNOMIAL: /* polynomial expression */
317                 {
318                         float *cp = NULL;
319                         char xval[32];
320                         unsigned int i;
321                         
322                         /* draw polynomial order selector */
323                         but= uiDefButS(block, NUM, B_FMODIFIER_REDRAW, "Poly Order: ", 10,cy,width-30,19, &data->poly_order, 1, 100, 0, 0, "'Order' of the Polynomial - for a polynomial with n terms, 'order' is n-1");
324                         uiButSetFunc(but, validate_fmodifier_cb, fcu, fcm);
325                         cy -= 35;
326                         
327                         /* draw controls for each coefficient and a + sign at end of row */
328                         uiDefBut(block, LABEL, 1, "y = ", 0, cy, 50, 20, NULL, 0.0, 0.0, 0, 0, "");
329                         
330                         cp= data->coefficients;
331                         for (i=0; (i < data->arraysize) && (cp); i++, cp++) {
332                                 /* coefficient */
333                                 uiDefButF(block, NUM, B_FMODIFIER_REDRAW, "", 50, cy, 150, 20, cp, -FLT_MAX, FLT_MAX, 10, 3, "Coefficient for polynomial");
334                                 
335                                 /* 'x' param (and '+' if necessary) */
336                                 if (i == 0)
337                                         strcpy(xval, "");
338                                 else if (i == 1)
339                                         strcpy(xval, "x");
340                                 else
341                                         sprintf(xval, "x^%d", i);
342                                 uiDefBut(block, LABEL, 1, xval, 200, cy, 50, 20, NULL, 0.0, 0.0, 0, 0, "Power of x");
343                                 
344                                 if ( (i != (data->arraysize - 1)) || ((i==0) && data->arraysize==2) )
345                                         uiDefBut(block, LABEL, 1, "+", 250, cy, 30, 20, NULL, 0.0, 0.0, 0, 0, "");
346                                 
347                                 cy -= 20;
348                         }
349                 }
350                         break;
351                 
352                 case FCM_GENERATOR_POLYNOMIAL_FACTORISED: /* factorised polynomial expression */
353                 {
354                         float *cp = NULL;
355                         unsigned int i;
356                         
357                         /* draw polynomial order selector */
358                         but= uiDefButS(block, NUM, B_FMODIFIER_REDRAW, "Poly Order: ", 10,cy,width-30,19, &data->poly_order, 1, 100, 0, 0, "'Order' of the Polynomial - for a polynomial with n terms, 'order' is n-1");
359                         uiButSetFunc(but, validate_fmodifier_cb, fcu, fcm);
360                         cy -= 35;
361                         
362                         /* draw controls for each pair of coefficients */
363                         uiDefBut(block, LABEL, 1, "y = ", 0, cy, 50, 20, NULL, 0.0, 0.0, 0, 0, "");
364                         
365                         cp= data->coefficients;
366                         for (i=0; (i < data->poly_order) && (cp); i++, cp+=2) {
367                                 /* opening bracket */
368                                 uiDefBut(block, LABEL, 1, "(", 40, cy, 50, 20, NULL, 0.0, 0.0, 0, 0, "");
369                                 
370                                 /* coefficients */
371                                 uiDefButF(block, NUM, B_FMODIFIER_REDRAW, "", 50, cy, 100, 20, cp, -FLT_MAX, FLT_MAX, 10, 3, "Coefficient of x");
372                                 
373                                 uiDefBut(block, LABEL, 1, "x + ", 150, cy, 30, 20, NULL, 0.0, 0.0, 0, 0, "");
374                                 
375                                 uiDefButF(block, NUM, B_FMODIFIER_REDRAW, "", 180, cy, 100, 20, cp+1, -FLT_MAX, FLT_MAX, 10, 3, "Second coefficient");
376                                 
377                                 /* closing bracket and '+' sign */
378                                 if ( (i != (data->poly_order - 1)) || ((i==0) && data->poly_order==2) )
379                                         uiDefBut(block, LABEL, 1, ") ×", 280, cy, 30, 20, NULL, 0.0, 0.0, 0, 0, "");
380                                 else
381                                         uiDefBut(block, LABEL, 1, ")", 280, cy, 30, 20, NULL, 0.0, 0.0, 0, 0, "");
382                                 
383                                 cy -= 20;
384                         }
385                 }
386                         break;
387                 
388                 case FCM_GENERATOR_FUNCTION: /* built-in function */
389                 {
390                         float *cp= data->coefficients;
391                         
392                         /* draw function selector */
393                         but= uiDefButS(block, MENU, B_FMODIFIER_REDRAW, fn_type, 10,cy,width-30,19, &data->func_type, 0, 0, 0, 0, "Built-In Function to use");
394                         uiButSetFunc(but, validate_fmodifier_cb, fcu, fcm);
395                         cy -= 35;
396                         
397                         /* draw controls for equation of coefficients */
398                         /* row 1 */
399                         {
400                                 uiDefBut(block, LABEL, 1, "y = ", 0, cy, 50, 20, NULL, 0.0, 0.0, 0, 0, "");
401                                 
402                                 uiDefButF(block, NUM, B_FMODIFIER_REDRAW, "", 50, cy, 150, 20, cp+3, -FLT_MAX, FLT_MAX, 10, 3, "Coefficient (D) for function");
403                                 uiDefBut(block, LABEL, 1, "+", 200, cy, 30, 20, NULL, 0.0, 0.0, 0, 0, "");
404                                 cy -= 20;
405                         }
406                         
407                         /* row 2 */
408                         {
409                                 char func_name[32];
410                                 
411                                 /* coefficient outside bracket */
412                                 uiDefButF(block, NUM, B_FMODIFIER_REDRAW, "", 0, cy, 80, 20, cp, -FLT_MAX, FLT_MAX, 10, 3, "Coefficient (A) for function");
413                                 
414                                 /* opening bracket */
415                                 switch (data->func_type)
416                                 {               
417                                         case FCM_GENERATOR_FN_SIN: /* sine wave */
418                                                 sprintf(func_name, "sin(");
419                                                 break;
420                                         case FCM_GENERATOR_FN_COS: /* cosine wave */
421                                                 sprintf(func_name, "cos(");
422                                                 break;
423                                         case FCM_GENERATOR_FN_TAN: /* tangent wave */
424                                                 sprintf(func_name, "tan(");
425                                                 break;
426                                         case FCM_GENERATOR_FN_LN: /* natural log */
427                                                 sprintf(func_name, "ln(");
428                                                 break;
429                                         case FCM_GENERATOR_FN_SQRT: /* square root */
430                                                 sprintf(func_name, "sqrt(");
431                                                 break;
432                                         default: /* unknown */
433                                                 sprintf(func_name, "<fn?>(");
434                                                 break;
435                                 }
436                                 uiDefBut(block, LABEL, 1, func_name, 80, cy, 40, 20, NULL, 0.0, 0.0, 0, 0, "");
437                                 
438                                 /* coefficients inside bracket */
439                                 uiDefButF(block, NUM, B_FMODIFIER_REDRAW, "", 115, cy, 75, 20, cp+1, -FLT_MAX, FLT_MAX, 10, 3, "Coefficient (B) of x");
440                                 
441                                 uiDefBut(block, LABEL, 1, "x+", 190, cy, 30, 20, NULL, 0.0, 0.0, 0, 0, "");
442                                 
443                                 uiDefButF(block, NUM, B_FMODIFIER_REDRAW, "", 220, cy, 80, 20, cp+2, -FLT_MAX, FLT_MAX, 10, 3, "Coefficient (C) of function");
444                                 
445                                 /* closing bracket */
446                                         uiDefBut(block, LABEL, 1, ")", 300, cy, 30, 20, NULL, 0.0, 0.0, 0, 0, "");
447                                 cy -= 20;
448                         }
449                 }
450                         break;
451                 
452                 case FCM_GENERATOR_EXPRESSION: /* py-expression */
453                         // TODO...
454                         break;
455         }
456 }
457
458 /* --------------- */
459
460 /* draw settings for cycles modifier */
461 static void draw_modifier__cycles(uiBlock *block, FCurve *fcu, FModifier *fcm, int *yco, short *height, short width, short active, int rb_col)
462 {
463         FMod_Cycles *data= (FMod_Cycles *)fcm->data;
464         char cyc_mode[]="Cycling Mode%t|No Cycles%x0|Repeat Motion%x1|Repeat with Offset%x2";
465         int cy= (*yco - 30), cy1= (*yco - 50), cy2= (*yco - 70);
466         
467         /* set the height */
468         (*height) = 90;
469         
470         /* basic settings (backdrop + some padding) */
471         //DRAW_BACKDROP((*height)); // XXX buggy...
472         
473         /* 'before' range */
474         uiDefBut(block, LABEL, 1, "Before:", 10, cy, 80, 20, NULL, 0.0, 0.0, 0, 0, "Settings for cycling before first keyframe");
475         uiBlockBeginAlign(block);
476                 uiDefButS(block, MENU, B_FMODIFIER_REDRAW, cyc_mode, 10,cy1,150,20, &data->before_mode, 0, 0, 0, 0, "Cycling mode to use before first keyframe");
477                 uiDefButS(block, NUM, B_FMODIFIER_REDRAW, "Max Cycles:", 10, cy2, 150, 20, &data->before_cycles, 0, 10000, 10, 3, "Maximum number of cycles to allow (0 = infinite)");
478         uiBlockEndAlign(block);
479         
480         /* 'after' range */
481         uiDefBut(block, LABEL, 1, "After:", 160, cy, 80, 20, NULL, 0.0, 0.0, 0, 0, "Settings for cycling after last keyframe");
482         uiBlockBeginAlign(block);
483                 uiDefButS(block, MENU, B_FMODIFIER_REDRAW, cyc_mode, 170,cy1,150,20, &data->after_mode, 0, 0, 0, 0, "Cycling mode to use after first keyframe");
484                 uiDefButS(block, NUM, B_FMODIFIER_REDRAW, "Max Cycles:", 170, cy2, 150, 20, &data->after_cycles, 0, 10000, 10, 3, "Maximum number of cycles to allow (0 = infinite)");
485         uiBlockEndAlign(block);
486 }
487
488 /* --------------- */
489
490 #define BINARYSEARCH_FRAMEEQ_THRESH     0.0001
491
492 /* Binary search algorithm for finding where to insert Envelope Data Point.
493  * Returns the index to insert at (data already at that index will be offset if replace is 0)
494  */
495 static int binarysearch_fcm_envelopedata_index (FCM_EnvelopeData array[], float frame, int arraylen, short *exists)
496 {
497         int start=0, end=arraylen;
498         int loopbreaker= 0, maxloop= arraylen * 2;
499         
500         /* initialise exists-flag first */
501         *exists= 0;
502         
503         /* sneaky optimisations (don't go through searching process if...):
504          *      - keyframe to be added is to be added out of current bounds
505          *      - keyframe to be added would replace one of the existing ones on bounds
506          */
507         if ((arraylen <= 0) || (array == NULL)) {
508                 printf("Warning: binarysearch_fcm_envelopedata_index() encountered invalid array \n");
509                 return 0;
510         }
511         else {
512                 /* check whether to add before/after/on */
513                 float framenum;
514                 
515                 /* 'First' Point (when only one point, this case is used) */
516                 framenum= array[0].time;
517                 if (IS_EQT(frame, framenum, BINARYSEARCH_FRAMEEQ_THRESH)) {
518                         *exists = 1;
519                         return 0;
520                 }
521                 else if (frame < framenum)
522                         return 0;
523                         
524                 /* 'Last' Point */
525                 framenum= array[(arraylen-1)].time;
526                 if (IS_EQT(frame, framenum, BINARYSEARCH_FRAMEEQ_THRESH)) {
527                         *exists= 1;
528                         return (arraylen - 1);
529                 }
530                 else if (frame > framenum)
531                         return arraylen;
532         }
533         
534         
535         /* most of the time, this loop is just to find where to put it
536          *      - 'loopbreaker' is just here to prevent infinite loops 
537          */
538         for (loopbreaker=0; (start <= end) && (loopbreaker < maxloop); loopbreaker++) {
539                 /* compute and get midpoint */
540                 int mid = start + ((end - start) / 2);  /* we calculate the midpoint this way to avoid int overflows... */
541                 float midfra= array[mid].time;
542                 
543                 /* check if exactly equal to midpoint */
544                 if (IS_EQT(frame, midfra, BINARYSEARCH_FRAMEEQ_THRESH)) {
545                         *exists = 1;
546                         return mid;
547                 }
548                 
549                 /* repeat in upper/lower half */
550                 if (frame > midfra)
551                         start= mid + 1;
552                 else if (frame < midfra)
553                         end= mid - 1;
554         }
555         
556         /* print error if loop-limit exceeded */
557         if (loopbreaker == (maxloop-1)) {
558                 printf("Error: binarysearch_fcm_envelopedata_index() was taking too long \n");
559                 
560                 // include debug info 
561                 printf("\tround = %d: start = %d, end = %d, arraylen = %d \n", loopbreaker, start, end, arraylen);
562         }
563         
564         /* not found, so return where to place it */
565         return start;
566 }
567
568 /* callback to add new envelope data point */
569 // TODO: should we have a separate file for things like this?
570 static void fmod_envelope_addpoint_cb (bContext *C, void *fcm_dv, void *dummy)
571 {
572         Scene *scene= CTX_data_scene(C);
573         FMod_Envelope *env= (FMod_Envelope *)fcm_dv;
574         FCM_EnvelopeData *fedn;
575         FCM_EnvelopeData fed;
576         
577         /* init template data */
578         fed.min= -1.0f;
579         fed.max= 1.0f;
580         fed.time= (float)scene->r.cfra; // XXX make this int for ease of use?
581         fed.f1= fed.f2= 0;
582         
583         /* check that no data exists for the current frame... */
584         if (env->data) {
585                 short exists = -1;
586                 int i= binarysearch_fcm_envelopedata_index(env->data, (float)(scene->r.cfra), env->totvert, &exists);
587                 
588                 /* binarysearch_...() will set exists by default to 0, so if it is non-zero, that means that the point exists already */
589                 if (exists)
590                         return;
591                         
592                 /* add new */
593                 fedn= MEM_callocN((env->totvert+1)*sizeof(FCM_EnvelopeData), "FCM_EnvelopeData");
594                 
595                 /* add the points that should occur before the point to be pasted */
596                 if (i > 0)
597                         memcpy(fedn, env->data, i*sizeof(FCM_EnvelopeData));
598                 
599                 /* add point to paste at index i */
600                 *(fedn + i)= fed;
601                 
602                 /* add the points that occur after the point to be pasted */
603                 if (i < env->totvert) 
604                         memcpy(fedn+i+1, env->data+i, (env->totvert-i)*sizeof(FCM_EnvelopeData));
605                 
606                 /* replace (+ free) old with new */
607                 MEM_freeN(env->data);
608                 env->data= fedn;
609                 
610                 env->totvert++;
611         }
612         else {
613                 env->data= MEM_callocN(sizeof(FCM_EnvelopeData), "FCM_EnvelopeData");
614                 *(env->data)= fed;
615                 
616                 env->totvert= 1;
617         }
618 }
619
620 /* callback to remove envelope data point */
621 // TODO: should we have a separate file for things like this?
622 static void fmod_envelope_deletepoint_cb (bContext *C, void *fcm_dv, void *ind_v)
623 {
624         FMod_Envelope *env= (FMod_Envelope *)fcm_dv;
625         FCM_EnvelopeData *fedn;
626         int index= GET_INT_FROM_POINTER(ind_v);
627         
628         /* check that no data exists for the current frame... */
629         if (env->totvert > 1) {
630                 /* allocate a new smaller array */
631                 fedn= MEM_callocN(sizeof(FCM_EnvelopeData)*(env->totvert-1), "FCM_EnvelopeData");
632                 
633                 memcpy(fedn, &env->data, sizeof(FCM_EnvelopeData)*(index));
634                 memcpy(&fedn[index], &env->data[index+1], sizeof(FCM_EnvelopeData)*(env->totvert-index-1));
635                 
636                 /* free old array, and set the new */
637                 MEM_freeN(env->data);
638                 env->data= fedn;
639                 env->totvert--;
640         }
641         else {
642                 /* just free array, since the only vert was deleted */
643                 if (env->data) 
644                         MEM_freeN(env->data);
645                 env->totvert= 0;
646         }
647 }
648
649 /* draw settings for envelope modifier */
650 static void draw_modifier__envelope(uiBlock *block, FCurve *fcu, FModifier *fcm, int *yco, short *height, short width, short active, int rb_col)
651 {
652         FMod_Envelope *env= (FMod_Envelope *)fcm->data;
653         FCM_EnvelopeData *fed;
654         uiBut *but;
655         int cy= (*yco - 30);
656         int i;
657         
658         /* set the height:
659          *      - basic settings + variable height from envelope controls
660          */
661         (*height) = 96 + (25 * env->totvert);
662         
663         /* basic settings (backdrop + general settings + some padding) */
664         //DRAW_BACKDROP((*height)); // XXX buggy...
665         
666         /* General Settings */
667         uiDefBut(block, LABEL, 1, "Envelope:", 10, cy, 100, 20, NULL, 0.0, 0.0, 0, 0, "Settings for cycling before first keyframe");
668         cy -= 20;
669         
670         uiBlockBeginAlign(block);
671                 uiDefButF(block, NUM, B_FMODIFIER_REDRAW, "Reference Val:", 10, cy, 300, 20, &env->midval, -FLT_MAX, FLT_MAX, 10, 3, "");
672                 cy -= 20;
673                 
674                 uiDefButF(block, NUM, B_FMODIFIER_REDRAW, "Min:", 10, cy, 150, 20, &env->min, -FLT_MAX, env->max, 10, 3, "Minimum value (relative to Reference Value) that is used as the 'normal' minimum value");
675                 uiDefButF(block, NUM, B_FMODIFIER_REDRAW, "Max:", 160, cy, 150, 20, &env->max, env->min, FLT_MAX, 10, 3, "Maximum value (relative to Reference Value) that is used as the 'normal' maximum value");
676                 cy -= 35;
677         uiBlockEndAlign(block);
678         
679         
680         /* Points header */
681         uiDefBut(block, LABEL, 1, "Control Points:", 10, cy, 150, 20, NULL, 0.0, 0.0, 0, 0, "");
682         
683         but= uiDefBut(block, BUT, B_FMODIFIER_REDRAW, "Add Point", 160,cy,150,19, NULL, 0, 0, 0, 0, "Adds a new control-point to the envelope on the current frame");
684         uiButSetFunc(but, fmod_envelope_addpoint_cb, env, NULL);
685         cy -= 35;
686         
687         /* Points List */
688         for (i=0, fed=env->data; i < env->totvert; i++, fed++) {
689                 uiBlockBeginAlign(block);
690                         but=uiDefButF(block, NUM, B_FMODIFIER_REDRAW, "Fra:", 5, cy, 100, 20, &fed->time, -FLT_MAX, FLT_MAX, 10, 3, "Frame that envelope point occurs");
691                         uiButSetFunc(but, validate_fmodifier_cb, fcu, fcm);
692                         
693                         uiDefButF(block, NUM, B_FMODIFIER_REDRAW, "Min:", 105, cy, 100, 20, &fed->min, -FLT_MAX, FLT_MAX, 10, 3, "Minimum bound of envelope at this point");
694                         uiDefButF(block, NUM, B_FMODIFIER_REDRAW, "Max:", 205, cy, 100, 20, &fed->max, -FLT_MAX, FLT_MAX, 10, 3, "Maximum bound of envelope at this point");
695                         
696                         but= uiDefIconBut(block, BUT, B_FMODIFIER_REDRAW, ICON_X, 305, cy, 20, 20, NULL, 0.0, 0.0, 0.0, 0.0, "Delete envelope control point");
697                         uiButSetFunc(but, fmod_envelope_deletepoint_cb, env, SET_INT_IN_POINTER(i));
698                 uiBlockBeginAlign(block);
699                 cy -= 25;
700         }
701 }
702
703 /* --------------- */
704
705 static void graph_panel_modifier_draw(uiBlock *block, FCurve *fcu, FModifier *fcm, int *yco)
706 {
707         FModifierTypeInfo *fmi= fmodifier_get_typeinfo(fcm);
708         uiBut *but;
709         short active= (fcm->flag & FMODIFIER_FLAG_ACTIVE);
710         short width= 314;
711         short height = 0; 
712         int rb_col;
713         
714         /* draw header */
715         {
716                 uiBlockSetEmboss(block, UI_EMBOSSN);
717                 
718                 /* rounded header */
719 #if 0 // XXX buggy...
720                 if (active) uiBlockSetCol(block, TH_BUT_ACTION);
721                         rb_col= (active)?-20:20;
722                         but= uiDefBut(block, ROUNDBOX, B_REDR, "", 10-8, *yco-2, width, 24, NULL, 5.0, 0.0, 15.0, (float)(rb_col-20), ""); 
723                 if (active) uiBlockSetCol(block, TH_AUTO);
724 #endif // XXX buggy
725                 
726                 /* expand */
727                 uiDefIconButBitS(block, ICONTOG, FMODIFIER_FLAG_EXPANDED, B_REDR, ICON_TRIA_RIGHT,      5, *yco-1, 20, 20, &fcm->flag, 0.0, 0.0, 0, 0, "Modifier is expanded");
728                 
729                 /* checkbox for 'active' status (for now) */
730                 but= uiDefIconButBitS(block, ICONTOG, FMODIFIER_FLAG_ACTIVE, B_REDR, ICON_RADIOBUT_OFF, 25, *yco-1, 20, 20, &fcm->flag, 0.0, 0.0, 0, 0, "Modifier is active one");
731                 uiButSetFunc(but, activate_fmodifier_cb, fcu, fcm);
732                 
733                 /* name */
734                 if (fmi)
735                         but= uiDefBut(block, LABEL, 1, fmi->name,       10+40, *yco, 240, 20, NULL, 0.0, 0.0, 0, 0, "F-Curve Modifier Type. Click to make modifier active one.");
736                 else
737                         but= uiDefBut(block, LABEL, 1, "<Unknown Modifier>",    10+40, *yco, 240, 20, NULL, 0.0, 0.0, 0, 0, "F-Curve Modifier Type. Click to make modifier active one.");
738                 
739                 /* delete button */
740                 but= uiDefIconBut(block, BUT, B_REDR, ICON_X, 10+(width-30), *yco, 19, 19, NULL, 0.0, 0.0, 0.0, 0.0, "Delete F-Curve Modifier.");
741                 uiButSetFunc(but, delete_fmodifier_cb, fcu, fcm);
742                 
743                 uiBlockSetEmboss(block, UI_EMBOSS);
744         }
745         
746         /* when modifier is expanded, draw settings */
747         if (fcm->flag & FMODIFIER_FLAG_EXPANDED) {
748                 /* draw settings for individual modifiers */
749                 switch (fcm->type) {
750                         case FMODIFIER_TYPE_GENERATOR: /* Generator */
751                                 draw_modifier__generator(block, fcu, fcm, yco, &height, width, active, rb_col);
752                                 break;
753                                 
754                         case FMODIFIER_TYPE_CYCLES: /* Cycles */
755                                 draw_modifier__cycles(block, fcu, fcm, yco, &height, width, active, rb_col);
756                                 break;
757                                 
758                         case FMODIFIER_TYPE_ENVELOPE: /* Envelope */
759                                 draw_modifier__envelope(block, fcu, fcm, yco, &height, width, active, rb_col);
760                                 break;
761                         
762                         default: /* unknown type */
763                                 height= 96;
764                                 //DRAW_BACKDROP(height); // XXX buggy...
765                                 break;
766                 }
767         }
768         
769         /* adjust height for new to start */
770         (*yco) -= (height + 27); 
771 }
772
773 static void graph_panel_modifiers(const bContext *C, ARegion *ar, short cntrl, bAnimListElem *ale)      
774 {
775         FCurve *fcu= (FCurve *)ale->data;
776         FModifier *fcm;
777         uiBlock *block;
778         int yco= 190;
779         
780         block= uiBeginBlock(C, ar, "graph_panel_modifiers", UI_EMBOSS, UI_HELV);
781         if (uiNewPanel(C, ar, block, "Modifiers", "Graph", 340, 30, 318, 254)==0) return;
782         uiBlockSetHandleFunc(block, do_graph_region_modifier_buttons, NULL);
783         
784         uiNewPanelHeight(block, 204);
785         
786         /* 'add modifier' button at top of panel */
787         // XXX for now, this will be a operator button which calls a temporary 'add modifier' operator
788         uiDefButO(block, BUT, "GRAPHEDIT_OT_fmodifier_add", WM_OP_INVOKE_REGION_WIN, "Add Modifier", 10, 225, 150, 20, "Adds a new F-Curve Modifier for the active F-Curve");
789         
790         /* draw each modifier */
791         for (fcm= fcu->modifiers.first; fcm; fcm= fcm->next)
792                 graph_panel_modifier_draw(block, fcu, fcm, &yco);
793         
794         /* since these buttons can have variable height */
795         if (yco < 0)
796                 uiNewPanelHeight(block, (204 - yco));
797         else
798                 uiNewPanelHeight(block, 204);
799 }
800
801 /* ******************* general ******************************** */
802
803 /* Find 'active' F-Curve. It must be editable, since that's the purpose of these buttons (subject to change).  
804  * We return the 'wrapper' since it contains valuable context info (about hierarchy), which will need to be freed 
805  * when the caller is done with it.
806  */
807 // TODO: move this to anim api with another name?
808 bAnimListElem *get_active_fcurve_channel (bAnimContext *ac)
809 {
810         ListBase anim_data = {NULL, NULL};
811         int filter= (ANIMFILTER_VISIBLE | ANIMFILTER_FOREDIT | ANIMFILTER_ACTIVE | ANIMFILTER_CURVESONLY);
812         int items = ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
813         
814         /* We take the first F-Curve only, since some other ones may have had 'active' flag set
815          * if they were from linked data.
816          */
817         if (items) {
818                 bAnimListElem *ale= (bAnimListElem *)anim_data.first;
819                 
820                 /* remove first item from list, then free the rest of the list and return the stored one */
821                 BLI_remlink(&anim_data, ale);
822                 BLI_freelistN(&anim_data);
823                 
824                 return ale;
825         }
826         
827         /* no active F-Curve */
828         return NULL;
829 }
830
831 void graph_region_buttons(const bContext *C, ARegion *ar)
832 {
833         SpaceIpo *sipo= (SpaceIpo *)CTX_wm_space_data(C);
834         bAnimContext ac;
835         bAnimListElem *ale= NULL;
836         
837         /* for now, only draw if we could init the anim-context info (necessary for all animation-related tools) 
838          * to work correctly is able to be correctly retrieved. There's no point showing empty panels?
839          */
840         if (ANIM_animdata_get_context(C, &ac) == 0) 
841                 return;
842         
843         
844         /* try to find 'active' F-Curve */
845         ale= get_active_fcurve_channel(&ac);
846         if (ale == NULL) 
847                 return; 
848                 
849         uiBeginPanels(C, ar);
850
851         /* for now, the properties panel displays info about the selected channels */
852         graph_panel_properties(C, ar, 0, ale);
853         
854         /* driver settings for active F-Curve (only for 'Drivers' mode) */
855         if (sipo->mode == SIPO_MODE_DRIVERS)
856                 graph_panel_drivers(C, ar, 0, ale);
857         
858         /* modifiers */
859         graph_panel_modifiers(C, ar, 0, ale);
860         
861
862         uiEndPanels(C, ar);
863         
864         /* free temp data */
865         MEM_freeN(ale);
866 }
867
868
869 static int graph_properties(bContext *C, wmOperator *op)
870 {
871         ScrArea *sa= CTX_wm_area(C);
872         ARegion *ar= graph_has_buttons_region(sa);
873         
874         if(ar) {
875                 ar->flag ^= RGN_FLAG_HIDDEN;
876                 ar->v2d.flag &= ~V2D_IS_INITIALISED; /* XXX should become hide/unhide api? */
877                 
878                 ED_area_initialize(CTX_wm_manager(C), CTX_wm_window(C), sa);
879                 ED_area_tag_redraw(sa);
880         }
881         return OPERATOR_FINISHED;
882 }
883
884 void GRAPHEDIT_OT_properties(wmOperatorType *ot)
885 {
886         ot->name= "Properties";
887         ot->idname= "GRAPHEDIT_OT_properties";
888         
889         ot->exec= graph_properties;
890         ot->poll= ED_operator_ipo_active; // xxx
891         
892         /* flags */
893         ot->flag= 0;
894 }