- use %u rather tham %d for unsigned ints in string formatting funcs.
[blender.git] / source / blender / editors / animation / fmodifier_ui.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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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 /** \file blender/editors/animation/fmodifier_ui.c
30  *  \ingroup edanimation
31  */
32
33
34 /* User-Interface Stuff for F-Modifiers:
35  * This file defines the (C-Coded) templates + editing callbacks needed 
36  * by the interface stuff or F-Modifiers, as used by F-Curves in the Graph Editor,
37  * and NLA-Strips in the NLA Editor.
38  *
39  * Copy/Paste Buffer for F-Modifiers:
40  * For now, this is also defined in this file so that it can be shared between the 
41  */
42  
43 #include <string.h>
44
45 #include "DNA_anim_types.h"
46 #include "DNA_scene_types.h"
47
48 #include "MEM_guardedalloc.h"
49
50 #include "BLI_blenlib.h"
51 #include "BLI_utildefines.h"
52
53 #include "BKE_context.h"
54 #include "BKE_fcurve.h"
55
56 #include "WM_api.h"
57 #include "WM_types.h"
58
59 #include "RNA_access.h"
60
61 #include "UI_interface.h"
62 #include "UI_resources.h"
63
64 #include "ED_anim_api.h"
65 #include "ED_util.h"
66
67 /* ********************************************** */
68 /* UI STUFF */
69
70 // XXX! --------------------------------
71 /* temporary definition for limits of float number buttons (FLT_MAX tends to infinity with old system) */
72 #define UI_FLT_MAX      10000.0f
73
74 #define B_REDR                                  1
75 #define B_FMODIFIER_REDRAW              20
76
77 /* macro for use here to draw background box and set height */
78 // XXX for now, roundbox has it's callback func set to NULL to not intercept events
79 #define DRAW_BACKDROP(height) \
80         { \
81                 uiDefBut(block, ROUNDBOX, B_REDR, "", -3, yco-height, width+3, height-1, NULL, 5.0, 0.0, 12.0, (float)rb_col, ""); \
82         }
83
84 /* callback to verify modifier data */
85 static void validate_fmodifier_cb (bContext *UNUSED(C), void *fcm_v, void *UNUSED(arg))
86 {
87         FModifier *fcm= (FModifier *)fcm_v;
88         FModifierTypeInfo *fmi= fmodifier_get_typeinfo(fcm);
89         
90         /* call the verify callback on the modifier if applicable */
91         if (fmi && fmi->verify_data)
92                 fmi->verify_data(fcm);
93 }
94
95 /* callback to remove the given modifier  */
96 static void delete_fmodifier_cb (bContext *C, void *fmods_v, void *fcm_v)
97 {
98         ListBase *modifiers = (ListBase *)fmods_v;
99         FModifier *fcm= (FModifier *)fcm_v;
100         
101         /* remove the given F-Modifier from the active modifier-stack */
102         remove_fmodifier(modifiers, fcm);
103
104         ED_undo_push(C, "Delete F-Curve Modifier");
105         
106         /* send notifiers */
107         // XXX for now, this is the only way to get updates in all the right places... but would be nice to have a special one in this case 
108         WM_event_add_notifier(C, NC_ANIMATION|ND_KEYFRAME|NA_EDITED, NULL);
109 }
110
111 /* --------------- */
112         
113 /* draw settings for generator modifier */
114 static void draw_modifier__generator(uiLayout *layout, ID *id, FModifier *fcm, short width)
115 {
116         FMod_Generator *data= (FMod_Generator *)fcm->data;
117         uiLayout *col, *row;
118         uiBlock *block;
119         uiBut *but;
120         PointerRNA ptr;
121         
122         /* init the RNA-pointer */
123         RNA_pointer_create(id, &RNA_FModifierFunctionGenerator, fcm, &ptr);
124         
125         /* basic settings (backdrop + mode selector + some padding) */
126         col= uiLayoutColumn(layout, 1);
127         block= uiLayoutGetBlock(layout);
128         uiBlockBeginAlign(block);
129                 but= uiDefButR(block, MENU, B_FMODIFIER_REDRAW, NULL, 0, 0, width-30, UI_UNIT_Y, &ptr, "mode", -1, 0, 0, -1, -1, NULL);
130                 uiButSetFunc(but, validate_fmodifier_cb, fcm, NULL);
131                 
132                 uiDefButR(block, TOG, B_FMODIFIER_REDRAW, NULL, 0, 0, width-30, UI_UNIT_Y, &ptr, "use_additive", -1, 0, 0, -1, -1, NULL);
133         uiBlockEndAlign(block);
134         
135         /* now add settings for individual modes */
136         switch (data->mode) {
137                 case FCM_GENERATOR_POLYNOMIAL: /* polynomial expression */
138                 {
139                         float *cp = NULL;
140                         char xval[32];
141                         unsigned int i;
142                         
143                         /* draw polynomial order selector */
144                         row= uiLayoutRow(layout, 0);
145                         block= uiLayoutGetBlock(row);
146                                 but= uiDefButI(block, NUM, B_FMODIFIER_REDRAW, "Poly Order: ", 10,0,width-30,19, &data->poly_order, 1, 100, 0, 0, "'Order' of the Polynomial - for a polynomial with n terms, 'order' is n-1");
147                                 uiButSetFunc(but, validate_fmodifier_cb, fcm, NULL);
148                         
149                         
150                         /* draw controls for each coefficient and a + sign at end of row */
151                         row= uiLayoutRow(layout, 1);
152                         block= uiLayoutGetBlock(row);
153                                 uiDefBut(block, LABEL, 1, "y = ", 0, 0, 50, 20, NULL, 0.0, 0.0, 0, 0, "");
154                         
155                         cp= data->coefficients;
156                         for (i=0; (i < data->arraysize) && (cp); i++, cp++) {
157                                 /* coefficient */
158                                 uiDefButF(block, NUM, B_FMODIFIER_REDRAW, "", 0, 0, 150, 20, cp, -UI_FLT_MAX, UI_FLT_MAX, 10, 3, "Coefficient for polynomial");
159                                 
160                                 /* 'x' param (and '+' if necessary) */
161                                 if (i) {
162                                         if (i == 1)
163                                                 strcpy(xval, "x");
164                                         else
165                                                 sprintf(xval, "x^%u", i);
166                                         uiDefBut(block, LABEL, 1, xval, 0, 0, 50, 20, NULL, 0.0, 0.0, 0, 0, "Power of x");
167                                 }
168                                 
169                                 if ( (i != (data->arraysize - 1)) || ((i==0) && data->arraysize==2) ) {
170                                         uiDefBut(block, LABEL, 1, "+", 0,0 , 30, 20, NULL, 0.0, 0.0, 0, 0, "");
171                                         
172                                         /* next coefficient on a new row */
173                                         row= uiLayoutRow(layout, 1);
174                                         block= uiLayoutGetBlock(row);
175                                 }
176                         }
177                 }
178                         break;
179                 
180                 case FCM_GENERATOR_POLYNOMIAL_FACTORISED: /* factorised polynomial expression */
181                 {
182                         float *cp = NULL;
183                         unsigned int i;
184                         
185                         /* draw polynomial order selector */
186                         row= uiLayoutRow(layout, 0);
187                         block= uiLayoutGetBlock(row);
188                                 but= uiDefButI(block, NUM, B_FMODIFIER_REDRAW, "Poly Order: ", 0,0,width-30,19, &data->poly_order, 1, 100, 0, 0, "'Order' of the Polynomial - for a polynomial with n terms, 'order' is n-1");
189                                 uiButSetFunc(but, validate_fmodifier_cb, fcm, NULL);
190                         
191                         
192                         /* draw controls for each pair of coefficients */
193                         row= uiLayoutRow(layout, 1);
194                         block= uiLayoutGetBlock(row);
195                                 uiDefBut(block, LABEL, 1, "y=", 0, 0, 50, 20, NULL, 0.0, 0.0, 0, 0, "");
196                         
197                         cp= data->coefficients;
198                         for (i=0; (i < data->poly_order) && (cp); i++, cp+=2) {
199                                 /* opening bracket */
200                                 uiDefBut(block, LABEL, 1, "(", 0, 0, 20, 20, NULL, 0.0, 0.0, 0, 0, "");
201                                 
202                                 /* coefficients */
203                                 uiDefButF(block, NUM, B_FMODIFIER_REDRAW, "", 0, 0, 100, 20, cp, -UI_FLT_MAX, UI_FLT_MAX, 10, 3, "Coefficient of x");
204                                 
205                                 uiDefBut(block, LABEL, 1, "x+", 0, 0, 40, 20, NULL, 0.0, 0.0, 0, 0, "");
206                                 
207                                 uiDefButF(block, NUM, B_FMODIFIER_REDRAW, "", 0, 0, 100, 20, cp+1, -UI_FLT_MAX, UI_FLT_MAX, 10, 3, "Second coefficient");
208                                 
209                                 /* closing bracket and '+' sign */
210                                 if ( (i != (data->poly_order - 1)) || ((i==0) && data->poly_order==2) ) {
211                                         uiDefBut(block, LABEL, 1, ") +", 0, 0, 30, 20, NULL, 0.0, 0.0, 0, 0, "");
212                                         
213                                         /* set up new row for the next pair of coefficients*/
214                                         row= uiLayoutRow(layout, 1);
215                                         block= uiLayoutGetBlock(row);
216                                 }
217                                 else 
218                                         uiDefBut(block, LABEL, 1, ")", 0, 0, 20, 20, NULL, 0.0, 0.0, 0, 0, "");
219                         }
220                 }
221                         break;
222         }
223 }
224
225 /* --------------- */
226
227 /* draw settings for generator modifier */
228 static void draw_modifier__fn_generator(uiLayout *layout, ID *id, FModifier *fcm, short UNUSED(width))
229 {
230         uiLayout *col;
231         PointerRNA ptr;
232         
233         /* init the RNA-pointer */
234         RNA_pointer_create(id, &RNA_FModifierFunctionGenerator, fcm, &ptr);
235         
236         /* add the settings */
237         col= uiLayoutColumn(layout, 1);
238                 uiItemR(col, &ptr, "function_type", 0, "", ICON_NONE);
239                 uiItemR(col, &ptr, "use_additive", UI_ITEM_R_TOGGLE, NULL, ICON_NONE);
240         
241         col= uiLayoutColumn(layout, 0); // no grouping for now
242                 uiItemR(col, &ptr, "amplitude", 0, NULL, ICON_NONE);
243                 uiItemR(col, &ptr, "phase_multiplier", 0, NULL, ICON_NONE);
244                 uiItemR(col, &ptr, "phase_offset", 0, NULL, ICON_NONE);
245                 uiItemR(col, &ptr, "value_offset", 0, NULL, ICON_NONE);
246 }
247
248 /* --------------- */
249
250 /* draw settings for cycles modifier */
251 static void draw_modifier__cycles(uiLayout *layout, ID *id, FModifier *fcm, short UNUSED(width))
252 {
253         uiLayout *split, *col;
254         PointerRNA ptr;
255         
256         /* init the RNA-pointer */
257         RNA_pointer_create(id, &RNA_FModifierCycles, fcm, &ptr);
258         
259         /* split into 2 columns 
260          * NOTE: the mode comboboxes shouldn't get labels, otherwise there isn't enough room
261          */
262         split= uiLayoutSplit(layout, 0.5f, 0);
263         
264         /* before range */
265         col= uiLayoutColumn(split, 1);
266                 uiItemL(col, "Before:", ICON_NONE);
267                 uiItemR(col, &ptr, "mode_before", 0, "", ICON_NONE);
268                 uiItemR(col, &ptr, "cycles_before", 0, NULL, ICON_NONE);
269                 
270         /* after range */
271         col= uiLayoutColumn(split, 1);
272                 uiItemL(col, "After:", ICON_NONE);
273                 uiItemR(col, &ptr, "mode_after", 0, "", ICON_NONE);
274                 uiItemR(col, &ptr, "cycles_after", 0, NULL, ICON_NONE);
275 }
276
277 /* --------------- */
278
279 /* draw settings for noise modifier */
280 static void draw_modifier__noise(uiLayout *layout, ID *id, FModifier *fcm, short UNUSED(width))
281 {
282         uiLayout *split, *col;
283         PointerRNA ptr;
284         
285         /* init the RNA-pointer */
286         RNA_pointer_create(id, &RNA_FModifierNoise, fcm, &ptr);
287         
288         /* blending mode */
289         uiItemR(layout, &ptr, "blend_type", 0, NULL, ICON_NONE);
290         
291         /* split into 2 columns */
292         split= uiLayoutSplit(layout, 0.5f, 0);
293         
294         /* col 1 */
295         col= uiLayoutColumn(split, 0);
296                 uiItemR(col, &ptr, "scale", 0, NULL, ICON_NONE);
297                 uiItemR(col, &ptr, "strength", 0, NULL, ICON_NONE);
298         
299         /* col 2 */
300         col= uiLayoutColumn(split, 0);
301                 uiItemR(col, &ptr, "phase", 0, NULL, ICON_NONE);
302                 uiItemR(col, &ptr, "depth", 0, NULL, ICON_NONE);
303 }
304
305 /* --------------- */
306
307 #define BINARYSEARCH_FRAMEEQ_THRESH     0.0001f
308
309 /* Binary search algorithm for finding where to insert Envelope Data Point.
310  * Returns the index to insert at (data already at that index will be offset if replace is 0)
311  */
312 static int binarysearch_fcm_envelopedata_index (FCM_EnvelopeData array[], float frame, int arraylen, short *exists)
313 {
314         int start=0, end=arraylen;
315         int loopbreaker= 0, maxloop= arraylen * 2;
316         
317         /* initialise exists-flag first */
318         *exists= 0;
319         
320         /* sneaky optimisations (don't go through searching process if...):
321          *      - keyframe to be added is to be added out of current bounds
322          *      - keyframe to be added would replace one of the existing ones on bounds
323          */
324         if ((arraylen <= 0) || (array == NULL)) {
325                 printf("Warning: binarysearch_fcm_envelopedata_index() encountered invalid array \n");
326                 return 0;
327         }
328         else {
329                 /* check whether to add before/after/on */
330                 float framenum;
331                 
332                 /* 'First' Point (when only one point, this case is used) */
333                 framenum= array[0].time;
334                 if (IS_EQT(frame, framenum, BINARYSEARCH_FRAMEEQ_THRESH)) {
335                         *exists = 1;
336                         return 0;
337                 }
338                 else if (frame < framenum)
339                         return 0;
340                         
341                 /* 'Last' Point */
342                 framenum= array[(arraylen-1)].time;
343                 if (IS_EQT(frame, framenum, BINARYSEARCH_FRAMEEQ_THRESH)) {
344                         *exists= 1;
345                         return (arraylen - 1);
346                 }
347                 else if (frame > framenum)
348                         return arraylen;
349         }
350         
351         
352         /* most of the time, this loop is just to find where to put it
353          *      - 'loopbreaker' is just here to prevent infinite loops 
354          */
355         for (loopbreaker=0; (start <= end) && (loopbreaker < maxloop); loopbreaker++) {
356                 /* compute and get midpoint */
357                 int mid = start + ((end - start) / 2);  /* we calculate the midpoint this way to avoid int overflows... */
358                 float midfra= array[mid].time;
359                 
360                 /* check if exactly equal to midpoint */
361                 if (IS_EQT(frame, midfra, BINARYSEARCH_FRAMEEQ_THRESH)) {
362                         *exists = 1;
363                         return mid;
364                 }
365                 
366                 /* repeat in upper/lower half */
367                 if (frame > midfra)
368                         start= mid + 1;
369                 else if (frame < midfra)
370                         end= mid - 1;
371         }
372         
373         /* print error if loop-limit exceeded */
374         if (loopbreaker == (maxloop-1)) {
375                 printf("Error: binarysearch_fcm_envelopedata_index() was taking too long \n");
376                 
377                 // include debug info 
378                 printf("\tround = %d: start = %d, end = %d, arraylen = %d \n", loopbreaker, start, end, arraylen);
379         }
380         
381         /* not found, so return where to place it */
382         return start;
383 }
384
385 /* callback to add new envelope data point */
386 // TODO: should we have a separate file for things like this?
387 static void fmod_envelope_addpoint_cb (bContext *C, void *fcm_dv, void *UNUSED(arg))
388 {
389         Scene *scene= CTX_data_scene(C);
390         FMod_Envelope *env= (FMod_Envelope *)fcm_dv;
391         FCM_EnvelopeData *fedn;
392         FCM_EnvelopeData fed;
393         
394         /* init template data */
395         fed.min= -1.0f;
396         fed.max= 1.0f;
397         fed.time= (float)scene->r.cfra; // XXX make this int for ease of use?
398         fed.f1= fed.f2= 0;
399         
400         /* check that no data exists for the current frame... */
401         if (env->data) {
402                 short exists = -1;
403                 int i= binarysearch_fcm_envelopedata_index(env->data, (float)(scene->r.cfra), env->totvert, &exists);
404                 
405                 /* binarysearch_...() will set exists by default to 0, so if it is non-zero, that means that the point exists already */
406                 if (exists)
407                         return;
408                         
409                 /* add new */
410                 fedn= MEM_callocN((env->totvert+1)*sizeof(FCM_EnvelopeData), "FCM_EnvelopeData");
411                 
412                 /* add the points that should occur before the point to be pasted */
413                 if (i > 0)
414                         memcpy(fedn, env->data, i*sizeof(FCM_EnvelopeData));
415                 
416                 /* add point to paste at index i */
417                 *(fedn + i)= fed;
418                 
419                 /* add the points that occur after the point to be pasted */
420                 if (i < env->totvert) 
421                         memcpy(fedn+i+1, env->data+i, (env->totvert-i)*sizeof(FCM_EnvelopeData));
422                 
423                 /* replace (+ free) old with new */
424                 MEM_freeN(env->data);
425                 env->data= fedn;
426                 
427                 env->totvert++;
428         }
429         else {
430                 env->data= MEM_callocN(sizeof(FCM_EnvelopeData), "FCM_EnvelopeData");
431                 *(env->data)= fed;
432                 
433                 env->totvert= 1;
434         }
435 }
436
437 /* callback to remove envelope data point */
438 // TODO: should we have a separate file for things like this?
439 static void fmod_envelope_deletepoint_cb (bContext *UNUSED(C), void *fcm_dv, void *ind_v)
440 {
441         FMod_Envelope *env= (FMod_Envelope *)fcm_dv;
442         FCM_EnvelopeData *fedn;
443         int index= GET_INT_FROM_POINTER(ind_v);
444         
445         /* check that no data exists for the current frame... */
446         if (env->totvert > 1) {
447                 /* allocate a new smaller array */
448                 fedn= MEM_callocN(sizeof(FCM_EnvelopeData)*(env->totvert-1), "FCM_EnvelopeData");
449
450                 memcpy(fedn, env->data, sizeof(FCM_EnvelopeData)*(index));
451                 memcpy(fedn + index, env->data + (index + 1), sizeof(FCM_EnvelopeData)*((env->totvert - index)-1));
452                 
453                 /* free old array, and set the new */
454                 MEM_freeN(env->data);
455                 env->data= fedn;
456                 env->totvert--;
457         }
458         else {
459                 /* just free array, since the only vert was deleted */
460                 if (env->data) {
461                         MEM_freeN(env->data);
462                         env->data= NULL;
463                 }
464                 env->totvert= 0;
465         }
466 }
467
468 /* draw settings for envelope modifier */
469 static void draw_modifier__envelope(uiLayout *layout, ID *id, FModifier *fcm, short UNUSED(width))
470 {
471         FMod_Envelope *env= (FMod_Envelope *)fcm->data;
472         FCM_EnvelopeData *fed;
473         uiLayout *col, *row;
474         uiBlock *block;
475         uiBut *but;
476         PointerRNA ptr;
477         int i;
478         
479         /* init the RNA-pointer */
480         RNA_pointer_create(id, &RNA_FModifierEnvelope, fcm, &ptr);
481         
482         /* general settings */
483         col= uiLayoutColumn(layout, 1);
484                 uiItemL(col, "Envelope:", ICON_NONE);
485                 uiItemR(col, &ptr, "reference_value", 0, NULL, ICON_NONE);
486                 
487                 row= uiLayoutRow(col, 1);
488                         uiItemR(row, &ptr, "default_min", 0, "Min", ICON_NONE);
489                         uiItemR(row, &ptr, "default_max", 0, "Max", ICON_NONE);
490                         
491         /* control points header */
492         // TODO: move this control-point control stuff to using the new special widgets for lists
493         // the current way is far too cramped
494         row= uiLayoutRow(layout, 0);
495         block= uiLayoutGetBlock(row);
496                 
497                 uiDefBut(block, LABEL, 1, "Control Points:", 0, 0, 150, 20, NULL, 0.0, 0.0, 0, 0, "");
498                 
499                 but= uiDefBut(block, BUT, B_FMODIFIER_REDRAW, "Add Point", 0,0,150,19, NULL, 0, 0, 0, 0, "Adds a new control-point to the envelope on the current frame");
500                 uiButSetFunc(but, fmod_envelope_addpoint_cb, env, NULL);
501                 
502         /* control points list */
503         for (i=0, fed=env->data; i < env->totvert; i++, fed++) {
504                 /* get a new row to operate on */
505                 row= uiLayoutRow(layout, 1);
506                 block= uiLayoutGetBlock(row);
507                 
508                 uiBlockBeginAlign(block);
509                         but=uiDefButF(block, NUM, B_FMODIFIER_REDRAW, "Fra:", 0, 0, 90, 20, &fed->time, -MAXFRAMEF, MAXFRAMEF, 10, 1, "Frame that envelope point occurs");
510                         uiButSetFunc(but, validate_fmodifier_cb, fcm, NULL);
511                         
512                         uiDefButF(block, NUM, B_FMODIFIER_REDRAW, "Min:", 0, 0, 100, 20, &fed->min, -UI_FLT_MAX, UI_FLT_MAX, 10, 2, "Minimum bound of envelope at this point");
513                         uiDefButF(block, NUM, B_FMODIFIER_REDRAW, "Max:", 0, 0, 100, 20, &fed->max, -UI_FLT_MAX, UI_FLT_MAX, 10, 2, "Maximum bound of envelope at this point");
514                         
515                         but= uiDefIconBut(block, BUT, B_FMODIFIER_REDRAW, ICON_X, 0, 0, 18, 20, NULL, 0.0, 0.0, 0.0, 0.0, "Delete envelope control point");
516                         uiButSetFunc(but, fmod_envelope_deletepoint_cb, env, SET_INT_IN_POINTER(i));
517                 uiBlockBeginAlign(block);
518         }
519 }
520
521 /* --------------- */
522
523 /* draw settings for limits modifier */
524 static void draw_modifier__limits(uiLayout *layout, ID *id, FModifier *fcm, short UNUSED(width))
525 {
526         uiLayout *split, *col, *row;
527         PointerRNA ptr;
528         
529         /* init the RNA-pointer */
530         RNA_pointer_create(id, &RNA_FModifierLimits, fcm, &ptr);
531         
532         /* row 1: minimum */
533         {
534                 row= uiLayoutRow(layout, 0);
535                 
536                 /* split into 2 columns */
537                 split= uiLayoutSplit(layout, 0.5f, 0);
538                 
539                 /* x-minimum */
540                 col= uiLayoutColumn(split, 1);
541                         uiItemR(col, &ptr, "use_min_x", 0, NULL, ICON_NONE);
542                         uiItemR(col, &ptr, "min_x", 0, NULL, ICON_NONE);
543                         
544                 /* y-minimum*/
545                 col= uiLayoutColumn(split, 1);
546                         uiItemR(col, &ptr, "use_min_y", 0, NULL, ICON_NONE);
547                         uiItemR(col, &ptr, "min_y", 0, NULL, ICON_NONE);
548         }
549         
550         /* row 2: maximum */
551         {
552                 row= uiLayoutRow(layout, 0);
553                 
554                 /* split into 2 columns */
555                 split= uiLayoutSplit(layout, 0.5f, 0);
556                 
557                 /* x-minimum */
558                 col= uiLayoutColumn(split, 1);
559                         uiItemR(col, &ptr, "use_max_x", 0, NULL, ICON_NONE);
560                         uiItemR(col, &ptr, "max_x", 0, NULL, ICON_NONE);
561                         
562                 /* y-minimum*/
563                 col= uiLayoutColumn(split, 1);
564                         uiItemR(col, &ptr, "use_max_y", 0, NULL, ICON_NONE);
565                         uiItemR(col, &ptr, "max_y", 0, NULL, ICON_NONE);
566         }
567 }
568
569 /* --------------- */
570
571 /* draw settings for stepped interpolation modifier */
572 static void draw_modifier__stepped(uiLayout *layout, ID *id, FModifier *fcm, short UNUSED(width))
573 {
574         uiLayout *col, *subcol;
575         PointerRNA ptr;
576         
577         /* init the RNA-pointer */
578         RNA_pointer_create(id, &RNA_FModifierStepped, fcm, &ptr);
579         
580         /* block 1: "stepping" settings */
581         col= uiLayoutColumn(layout, 0);
582                 uiItemR(col, &ptr, "frame_step", 0, NULL, ICON_NONE);
583                 uiItemR(col, &ptr, "frame_offset", 0, NULL, ICON_NONE);
584                 
585         /* block 2: start range settings */
586         col= uiLayoutColumn(layout, 1);
587                 uiItemR(col, &ptr, "use_frame_start", 0, NULL, ICON_NONE);
588                 
589                 subcol = uiLayoutColumn(col, 1);
590                 uiLayoutSetActive(subcol, RNA_boolean_get(&ptr, "use_frame_start"));
591                         uiItemR(subcol, &ptr, "frame_start", 0, NULL, ICON_NONE);
592                         
593         /* block 3: end range settings */
594         col= uiLayoutColumn(layout, 1);
595                 uiItemR(col, &ptr, "use_frame_end", 0, NULL, ICON_NONE);
596                 
597                 subcol = uiLayoutColumn(col, 1);
598                 uiLayoutSetActive(subcol, RNA_boolean_get(&ptr, "use_frame_end"));
599                         uiItemR(subcol, &ptr, "frame_end", 0, NULL, ICON_NONE);
600 }
601
602 /* --------------- */
603
604 void ANIM_uiTemplate_fmodifier_draw (uiLayout *layout, ID *id, ListBase *modifiers, FModifier *fcm)
605 {
606         FModifierTypeInfo *fmi= fmodifier_get_typeinfo(fcm);
607         uiLayout *box, *row, *subrow;
608         uiBlock *block;
609         uiBut *but;
610         short width= 314;
611         PointerRNA ptr;
612         
613         /* init the RNA-pointer */
614         RNA_pointer_create(id, &RNA_FModifier, fcm, &ptr);
615         
616         /* draw header */
617         {
618                 /* get layout-row + UI-block for this */
619                 box= uiLayoutBox(layout);
620                 
621                 row= uiLayoutRow(box, 0);
622                 block= uiLayoutGetBlock(row); // err...
623                 
624                 /* left-align -------------------------------------------- */
625                 subrow= uiLayoutRow(row, 0);
626                 uiLayoutSetAlignment(subrow, UI_LAYOUT_ALIGN_LEFT);
627                 
628                 uiBlockSetEmboss(block, UI_EMBOSSN);
629                 
630                 /* expand */
631                 uiItemR(subrow, &ptr, "show_expanded", UI_ITEM_R_ICON_ONLY, "", ICON_NONE);
632                 
633                 /* checkbox for 'active' status (for now) */
634                 uiItemR(subrow, &ptr, "active", UI_ITEM_R_ICON_ONLY, "", ICON_NONE);
635                 
636                 /* name */
637                 if (fmi)
638                         uiItemL(subrow, fmi->name, ICON_NONE);
639                 else
640                         uiItemL(subrow, "<Unknown Modifier>", ICON_NONE);
641                 
642                 /* right-align ------------------------------------------- */
643                 subrow= uiLayoutRow(row, 0);
644                 uiLayoutSetAlignment(subrow, UI_LAYOUT_ALIGN_RIGHT);
645                 
646                 
647                 /* 'mute' button */
648                 uiItemR(subrow, &ptr, "mute", UI_ITEM_R_ICON_ONLY, "", ICON_NONE);
649                 
650                 uiBlockSetEmboss(block, UI_EMBOSSN);
651                 
652                 /* delete button */
653                 but= uiDefIconBut(block, BUT, B_REDR, ICON_X, 0, 0, UI_UNIT_X, UI_UNIT_Y, NULL, 0.0, 0.0, 0.0, 0.0, "Delete F-Curve Modifier.");
654                 uiButSetFunc(but, delete_fmodifier_cb, modifiers, fcm);
655                 
656                 uiBlockSetEmboss(block, UI_EMBOSS);
657         }
658         
659         /* when modifier is expanded, draw settings */
660         if (fcm->flag & FMODIFIER_FLAG_EXPANDED) {
661                 /* set up the flexible-box layout which acts as the backdrop for the modifier settings */
662                 box= uiLayoutBox(layout); 
663                 
664                 /* draw settings for individual modifiers */
665                 switch (fcm->type) {
666                         case FMODIFIER_TYPE_GENERATOR: /* Generator */
667                                 draw_modifier__generator(box, id, fcm, width);
668                                 break;
669                                 
670                         case FMODIFIER_TYPE_FN_GENERATOR: /* Built-In Function Generator */
671                                 draw_modifier__fn_generator(box, id, fcm, width);
672                                 break;
673                                 
674                         case FMODIFIER_TYPE_CYCLES: /* Cycles */
675                                 draw_modifier__cycles(box, id, fcm, width);
676                                 break;
677                                 
678                         case FMODIFIER_TYPE_ENVELOPE: /* Envelope */
679                                 draw_modifier__envelope(box, id, fcm, width);
680                                 break;
681                                 
682                         case FMODIFIER_TYPE_LIMITS: /* Limits */
683                                 draw_modifier__limits(box, id, fcm, width);
684                                 break;
685                         
686                         case FMODIFIER_TYPE_NOISE: /* Noise */
687                                 draw_modifier__noise(box, id, fcm, width);
688                                 break;
689                                 
690                         case FMODIFIER_TYPE_STEPPED: /* Stepped */
691                                 draw_modifier__stepped(box, id, fcm, width);
692                                 break;
693                         
694                         default: /* unknown type */
695                                 break;
696                 }
697         }
698 }
699
700 /* ********************************************** */
701 /* COPY/PASTE BUFFER STUFF */
702
703 /* Copy/Paste Buffer itself (list of FModifier 's) */
704 static ListBase fmodifier_copypaste_buf = {NULL, NULL};
705
706 /* ---------- */
707
708 /* free the copy/paste buffer */
709 void free_fmodifiers_copybuf (void)
710 {
711         /* just free the whole buffer */
712         free_fmodifiers(&fmodifier_copypaste_buf);
713 }
714
715 /* copy the given F-Modifiers to the buffer, returning whether anything was copied or not
716  * assuming that the buffer has been cleared already with free_fmodifiers_copybuf()
717  *      - active: only copy the active modifier
718  */
719 short ANIM_fmodifiers_copy_to_buf (ListBase *modifiers, short active)
720 {
721         short ok = 1;
722         
723         /* sanity checks */
724         if ELEM(NULL, modifiers, modifiers->first)
725                 return 0;
726                 
727         /* copy the whole list, or just the active one? */
728         if (active) {
729                 FModifier *fcm = find_active_fmodifier(modifiers);
730                 
731                 if (fcm) {
732                         FModifier *fcmN = copy_fmodifier(fcm);
733                         BLI_addtail(&fmodifier_copypaste_buf, fcmN);
734                 }
735                 else
736                         ok = 0;
737         }
738         else
739                 copy_fmodifiers(&fmodifier_copypaste_buf, modifiers);
740                 
741         /* did we succeed? */
742         return ok;
743 }
744
745 /* 'Paste' the F-Modifier(s) from the buffer to the specified list 
746  *      - replace: free all the existing modifiers to leave only the pasted ones 
747  */
748 short ANIM_fmodifiers_paste_from_buf (ListBase *modifiers, short replace)
749 {
750         FModifier *fcm;
751         short ok = 0;
752         
753         /* sanity checks */
754         if (modifiers == NULL)
755                 return 0;
756                 
757         /* if replacing the list, free the existing modifiers */
758         if (replace)
759                 free_fmodifiers(modifiers);
760                 
761         /* now copy over all the modifiers in the buffer to the end of the list */
762         for (fcm= fmodifier_copypaste_buf.first; fcm; fcm= fcm->next) {
763                 /* make a copy of it */
764                 FModifier *fcmN = copy_fmodifier(fcm);
765                 
766                 /* make sure the new one isn't active, otherwise the list may get several actives */
767                 fcmN->flag &= ~FMODIFIER_FLAG_ACTIVE;
768                 
769                 /* now add it to the end of the list */
770                 BLI_addtail(modifiers, fcmN);
771                 ok = 1;
772         }
773         
774         /* did we succeed? */
775         return ok;
776 }
777
778 /* ********************************************** */