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