Graph Editor Drawing Tweaks:
[blender-staging.git] / source / blender / editors / animation / drivers.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, Joshua Leung
21  * All rights reserved.
22  *
23  * The Original Code is: all of this file.
24  *
25  * Contributor(s): Joshua Leung (full recode)
26  *
27  * ***** END GPL LICENSE BLOCK *****
28  */
29  
30 #include <stdio.h>
31 #include <stddef.h>
32 #include <string.h>
33 #include <math.h>
34 #include <float.h>
35
36 #include "MEM_guardedalloc.h"
37
38 #include "BLI_blenlib.h"
39 #include "BLI_arithb.h"
40 #include "BLI_dynstr.h"
41
42 #include "DNA_anim_types.h"
43 #include "DNA_action_types.h"
44 #include "DNA_armature_types.h"
45 #include "DNA_constraint_types.h"
46 #include "DNA_key_types.h"
47 #include "DNA_object_types.h"
48 #include "DNA_material_types.h"
49 #include "DNA_scene_types.h"
50 #include "DNA_userdef_types.h"
51 #include "DNA_windowmanager_types.h"
52
53 #include "BKE_animsys.h"
54 #include "BKE_action.h"
55 #include "BKE_constraint.h"
56 #include "BKE_fcurve.h"
57 #include "BKE_utildefines.h"
58 #include "BKE_context.h"
59 #include "BKE_report.h"
60 #include "BKE_key.h"
61 #include "BKE_material.h"
62
63 #include "ED_anim_api.h"
64 #include "ED_keyframing.h"
65 #include "ED_keyframes_edit.h"
66 #include "ED_screen.h"
67 #include "ED_util.h"
68
69 #include "UI_interface.h"
70
71 #include "WM_api.h"
72 #include "WM_types.h"
73
74 #include "RNA_access.h"
75 #include "RNA_define.h"
76 #include "RNA_types.h"
77
78 /* ************************************************** */
79 /* Animation Data Validation */
80
81 /* Get (or add relevant data to be able to do so) F-Curve from the driver stack, 
82  * for the given Animation Data block. This assumes that all the destinations are valid.
83  *      
84  *      - add:  0 - don't add anything if not found, 
85  *                      1 - add new Driver FCurve, 
86  *                      -1 - add new Driver FCurve without driver stuff (for pasting)
87  */
88 FCurve *verify_driver_fcurve (ID *id, const char rna_path[], const int array_index, short add)
89 {
90         AnimData *adt;
91         FCurve *fcu;
92         
93         /* sanity checks */
94         if ELEM(NULL, id, rna_path)
95                 return NULL;
96         
97         /* init animdata if none available yet */
98         adt= BKE_animdata_from_id(id);
99         if ((adt == NULL) && (add))
100                 adt= BKE_id_add_animdata(id);
101         if (adt == NULL) { 
102                 /* if still none (as not allowed to add, or ID doesn't have animdata for some reason) */
103                 return NULL;
104         }
105                 
106         /* try to find f-curve matching for this setting 
107          *      - add if not found and allowed to add one
108          *              TODO: add auto-grouping support? how this works will need to be resolved
109          */
110         fcu= list_find_fcurve(&adt->drivers, rna_path, array_index);
111         
112         if ((fcu == NULL) && (add)) {
113                 /* use default settings to make a F-Curve */
114                 fcu= MEM_callocN(sizeof(FCurve), "FCurve");
115                 
116                 fcu->flag = (FCURVE_VISIBLE|FCURVE_AUTO_HANDLES|FCURVE_SELECTED);
117                 
118                 /* store path - make copy, and store that */
119                 fcu->rna_path= BLI_strdupn(rna_path, strlen(rna_path));
120                 fcu->array_index= array_index;
121                 
122                 /* if add is negative, don't init this data yet, since it will be filled in by the pasted driver */
123                 if (add > 0) {
124                         /* add some new driver data */
125                         fcu->driver= MEM_callocN(sizeof(ChannelDriver), "ChannelDriver");
126                         
127                         /* add simple generator modifier for driver so that there is some visible representation */
128                         add_fmodifier(&fcu->modifiers, FMODIFIER_TYPE_GENERATOR);
129                 }
130                 
131                 /* just add F-Curve to end of driver list */
132                 BLI_addtail(&adt->drivers, fcu);
133         }
134         
135         /* return the F-Curve */
136         return fcu;
137 }
138
139 /* ************************************************** */
140 /* Driver Management API */
141
142 /* Main Driver Management API calls:
143  *      Add a new driver for the specified property on the given ID block
144  */
145 short ANIM_add_driver (ID *id, const char rna_path[], int array_index, short flag, int type)
146 {       
147         PointerRNA id_ptr, ptr;
148         PropertyRNA *prop;
149         FCurve *fcu;
150         int array_index_max = array_index+1;
151         int done = 0;
152         
153         /* validate pointer first - exit if failure */
154         RNA_id_pointer_create(id, &id_ptr);
155         if ((RNA_path_resolve(&id_ptr, rna_path, &ptr, &prop) == 0) || (prop == NULL)) {
156                 printf("Add Driver: Could not add Driver, as RNA Path is invalid for the given ID (ID = %s, Path = %s)\n", id->name, rna_path);
157                 return 0;
158         }
159         
160         /* key entire array convenience method */
161         if (array_index == -1) {
162                 array_index= 0;
163                 array_index_max= RNA_property_array_length(&ptr, prop) + 1;
164         }
165         
166         /* will only loop once unless the array index was -1 */
167         for (; array_index < array_index_max; array_index++) {
168                 /* create F-Curve with Driver */
169                 fcu= verify_driver_fcurve(id, rna_path, array_index, 1);
170                 
171                 if (fcu && fcu->driver) {
172                         fcu->driver->type= type;
173                         
174                         /* fill in current value for python */
175                         if (type == DRIVER_TYPE_PYTHON) {
176                                 PropertyType proptype= RNA_property_type(prop);
177                                 int array= RNA_property_array_length(&ptr, prop);
178                                 char *expression= fcu->driver->expression;
179                                 int val, maxlen= sizeof(fcu->driver->expression);
180                                 float fval;
181                                 
182                                 if (proptype == PROP_BOOLEAN) {
183                                         if (!array) val= RNA_property_boolean_get(&ptr, prop);
184                                         else val= RNA_property_boolean_get_index(&ptr, prop, array_index);
185                                         
186                                         BLI_strncpy(expression, (val)? "True": "False", maxlen);
187                                 }
188                                 else if (proptype == PROP_INT) {
189                                         if (!array) val= RNA_property_int_get(&ptr, prop);
190                                         else val= RNA_property_int_get_index(&ptr, prop, array_index);
191                                         
192                                         BLI_snprintf(expression, maxlen, "%d", val);
193                                 }
194                                 else if (proptype == PROP_FLOAT) {
195                                         if (!array) fval= RNA_property_float_get(&ptr, prop);
196                                         else fval= RNA_property_float_get_index(&ptr, prop, array_index);
197                                         
198                                         BLI_snprintf(expression, maxlen, "%.3f", fval);
199                                 }
200                         }
201                 }
202                 
203                 /* set the done status */
204                 done += (fcu != NULL);
205         }
206         
207         /* done */
208         return done;
209 }
210
211 /* Main Driver Management API calls:
212  *      Remove the driver for the specified property on the given ID block (if available)
213  */
214 short ANIM_remove_driver (struct ID *id, const char rna_path[], int array_index, short flag)
215 {
216         AnimData *adt;
217         FCurve *fcu;
218         
219         /* get F-Curve
220          * Note: here is one of the places where we don't want new F-Curve + Driver added!
221          *              so 'add' var must be 0
222          */
223         /* we don't check the validity of the path here yet, but it should be ok... */
224         fcu= verify_driver_fcurve(id, rna_path, array_index, 0);
225         adt= BKE_animdata_from_id(id);
226         
227         /* only continue if we have an driver to remove */
228         if (adt && fcu) {
229                 /* remove F-Curve from driver stack, then free it */
230                 BLI_remlink(&adt->drivers, fcu);
231                 free_fcurve(fcu);
232                 
233                 /* done successfully */
234                 return 1;
235         }
236         
237         /* failed */
238         return 0;
239 }
240
241 /* ************************************************** */
242 /* Driver Management API - Copy/Paste Drivers */
243
244 /* Copy/Paste Buffer for Driver Data... */
245 static FCurve *channeldriver_copypaste_buf = NULL;
246
247 /* This function frees any MEM_calloc'ed copy/paste buffer data */
248 // XXX find some header to put this in!
249 void free_anim_drivers_copybuf (void)
250 {
251         /* free the buffer F-Curve if it exists, as if it were just another F-Curve */
252         if (channeldriver_copypaste_buf)
253                 free_fcurve(channeldriver_copypaste_buf);
254         channeldriver_copypaste_buf= NULL;
255 }
256
257 /* Checks if there is a driver in the copy/paste buffer */
258 short ANIM_driver_can_paste (void)
259 {
260         return (channeldriver_copypaste_buf != NULL);
261 }
262
263 /* ------------------- */
264
265 /* Main Driver Management API calls:
266  *      Make a copy of the driver for the specified property on the given ID block
267  */
268 short ANIM_copy_driver (ID *id, const char rna_path[], int array_index, short flag)
269 {
270         PointerRNA id_ptr, ptr;
271         PropertyRNA *prop;
272         FCurve *fcu;
273         
274         /* validate pointer first - exit if failure */
275         RNA_id_pointer_create(id, &id_ptr);
276         if ((RNA_path_resolve(&id_ptr, rna_path, &ptr, &prop) == 0) || (prop == NULL)) {
277                 printf("Copy Driver: Could not find Driver, as RNA Path is invalid for the given ID (ID = %s, Path = %s)\n", id->name, rna_path);
278                 return 0;
279         }
280         
281         /* try to get F-Curve with Driver */
282         fcu= verify_driver_fcurve(id, rna_path, array_index, 0);
283         
284         /* clear copy/paste buffer first (for consistency with other copy/paste buffers) */
285         free_anim_drivers_copybuf();
286         
287         /* copy this to the copy/paste buf if it exists */
288         if (fcu && fcu->driver) {
289                 /* make copies of some info such as the rna_path, then clear this info from the F-Curve temporarily
290                  * so that we don't end up wasting memory storing the path which won't get used ever...
291                  */
292                 char *tmp_path = fcu->rna_path;
293                 fcu->rna_path= NULL;
294                 
295                 /* make a copy of the F-Curve with */
296                 channeldriver_copypaste_buf= copy_fcurve(fcu);
297                 
298                 /* restore the path */
299                 fcu->rna_path= tmp_path;
300                 
301                 /* copied... */
302                 return 1;
303         }
304         
305         /* done */
306         return 0;
307 }
308
309 /* Main Driver Management API calls:
310  *      Add a new driver for the specified property on the given ID block or replace an existing one
311  *      with the driver + driver-curve data from the buffer 
312  */
313 short ANIM_paste_driver (ID *id, const char rna_path[], int array_index, short flag)
314 {       
315         PointerRNA id_ptr, ptr;
316         PropertyRNA *prop;
317         FCurve *fcu;
318         
319         /* validate pointer first - exit if failure */
320         RNA_id_pointer_create(id, &id_ptr);
321         if ((RNA_path_resolve(&id_ptr, rna_path, &ptr, &prop) == 0) || (prop == NULL)) {
322                 printf("Paste Driver: Could not add Driver, as RNA Path is invalid for the given ID (ID = %s, Path = %s)\n", id->name, rna_path);
323                 return 0;
324         }
325         
326         /* if the buffer is empty, cannot paste... */
327         if (channeldriver_copypaste_buf == NULL) {
328                 printf("Paste Driver: No Driver to paste. \n");
329                 return 0;
330         }
331         
332         /* create Driver F-Curve, but without data which will be copied across... */
333         fcu= verify_driver_fcurve(id, rna_path, array_index, -1);
334
335         if (fcu) {
336                 /* copy across the curve data from the buffer curve 
337                  * NOTE: this step needs care to not miss new settings
338                  */
339                         /* keyframes/samples */
340                 fcu->bezt= MEM_dupallocN(channeldriver_copypaste_buf->bezt);
341                 fcu->fpt= MEM_dupallocN(channeldriver_copypaste_buf->fpt);
342                 fcu->totvert= channeldriver_copypaste_buf->totvert;
343                 
344                         /* modifiers */
345                 copy_fmodifiers(&fcu->modifiers, &channeldriver_copypaste_buf->modifiers);
346                 
347                         /* flags - on a per-relevant-flag basis */
348                 if (channeldriver_copypaste_buf->flag & FCURVE_AUTO_HANDLES)
349                         fcu->flag |= FCURVE_AUTO_HANDLES;
350                 else
351                         fcu->flag &= ~FCURVE_AUTO_HANDLES;
352                         /* extrapolation mode */
353                 fcu->extend= channeldriver_copypaste_buf->extend;
354                         
355                         /* the 'juicy' stuff - the driver */
356                 fcu->driver= fcurve_copy_driver(channeldriver_copypaste_buf->driver);
357         }
358         
359         /* done */
360         return (fcu != NULL);
361 }
362
363 /* ************************************************** */
364 /* UI-Button Interface */
365
366 /* Add Driver Button Operator ------------------------ */
367
368 static int add_driver_button_exec (bContext *C, wmOperator *op)
369 {
370         PointerRNA ptr;
371         PropertyRNA *prop= NULL;
372         char *path;
373         short success= 0;
374         int index, all= RNA_boolean_get(op->ptr, "all");
375         
376         /* try to create driver using property retrieved from UI */
377         memset(&ptr, 0, sizeof(PointerRNA));
378         uiAnimContextProperty(C, &ptr, &prop, &index);
379
380         if (all)
381                 index= -1;
382
383         if (ptr.data && prop && RNA_property_animateable(ptr.data, prop)) {
384                 path= RNA_path_from_ID_to_property(&ptr, prop);
385                 
386                 if (path) {                     
387                         success+= ANIM_add_driver(ptr.id.data, path, index, 0, DRIVER_TYPE_PYTHON);
388                         
389                         MEM_freeN(path);
390                 }
391         }
392         
393         if (success) {
394                 /* send updates */
395                 ED_anim_dag_flush_update(C);    
396                 
397                 /* for now, only send ND_KEYS for KeyingSets */
398                 WM_event_add_notifier(C, ND_KEYS, NULL); // XXX
399         }
400         
401         return (success)? OPERATOR_FINISHED: OPERATOR_CANCELLED;
402 }
403
404 void ANIM_OT_add_driver_button (wmOperatorType *ot)
405 {
406         /* identifiers */
407         ot->name= "Add Driver";
408         ot->idname= "ANIM_OT_add_driver_button";
409         ot->description= "Add driver(s) for the property(s) connected represented by the highlighted button.";
410         
411         /* callbacks */
412         ot->exec= add_driver_button_exec; 
413         //op->poll= ??? // TODO: need to have some animateable property to do this
414         
415         /* flags */
416         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
417
418         /* properties */
419         RNA_def_boolean(ot->srna, "all", 1, "All", "Create drivers for all elements of the array.");
420 }
421
422 /* Remove Driver Button Operator ------------------------ */
423
424 static int remove_driver_button_exec (bContext *C, wmOperator *op)
425 {
426         PointerRNA ptr;
427         PropertyRNA *prop= NULL;
428         char *path;
429         short success= 0;
430         int a, index, length, all= RNA_boolean_get(op->ptr, "all");
431         
432         /* try to find driver using property retrieved from UI */
433         memset(&ptr, 0, sizeof(PointerRNA));
434         uiAnimContextProperty(C, &ptr, &prop, &index);
435
436         if (ptr.data && prop) {
437                 path= RNA_path_from_ID_to_property(&ptr, prop);
438                 
439                 if (path) {
440                         if (all) {
441                                 length= RNA_property_array_length(&ptr, prop);
442                                 
443                                 if(length) index= 0;
444                                 else length= 1;
445                         }
446                         else
447                                 length= 1;
448                         
449                         for (a=0; a<length; a++)
450                                 success+= ANIM_remove_driver(ptr.id.data, path, index+a, 0);
451                         
452                         MEM_freeN(path);
453                 }
454         }
455         
456         
457         if (success) {
458                 /* send updates */
459                 ED_anim_dag_flush_update(C);    
460                 
461                 /* for now, only send ND_KEYS for KeyingSets */
462                 WM_event_add_notifier(C, ND_KEYS, NULL);  // XXX
463         }
464         
465         return (success)? OPERATOR_FINISHED: OPERATOR_CANCELLED;
466 }
467
468 void ANIM_OT_remove_driver_button (wmOperatorType *ot)
469 {
470         /* identifiers */
471         ot->name= "Remove Driver";
472         ot->idname= "ANIM_OT_remove_driver_button";
473         ot->description= "Remove the driver(s) for the property(s) connected represented by the highlighted button.";
474         
475         /* callbacks */
476         ot->exec= remove_driver_button_exec; 
477         //op->poll= ??? // TODO: need to have some driver to be able to do this...
478         
479         /* flags */
480         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
481
482         /* properties */
483         RNA_def_boolean(ot->srna, "all", 1, "All", "Delete drivers for all elements of the array.");
484 }
485
486 /* Copy Driver Button Operator ------------------------ */
487
488 static int copy_driver_button_exec (bContext *C, wmOperator *op)
489 {
490         PointerRNA ptr;
491         PropertyRNA *prop= NULL;
492         char *path;
493         short success= 0;
494         int index;
495         
496         /* try to create driver using property retrieved from UI */
497         memset(&ptr, 0, sizeof(PointerRNA));
498         uiAnimContextProperty(C, &ptr, &prop, &index);
499         
500         if (ptr.data && prop && RNA_property_animateable(ptr.data, prop)) {
501                 path= RNA_path_from_ID_to_property(&ptr, prop);
502                 
503                 if (path) {
504                         /* only copy the driver for the button that this was involved for */
505                         success= ANIM_copy_driver(ptr.id.data, path, index, 0);
506                         
507                         MEM_freeN(path);
508                 }
509         }
510         
511         /* since we're just copying, we don't really need to do anything else...*/
512         return (success)? OPERATOR_FINISHED: OPERATOR_CANCELLED;
513 }
514
515 void ANIM_OT_copy_driver_button (wmOperatorType *ot)
516 {
517         /* identifiers */
518         ot->name= "Copy Driver";
519         ot->idname= "ANIM_OT_copy_driver_button";
520         ot->description= "Copy the driver for the highlighted button.";
521         
522         /* callbacks */
523         ot->exec= copy_driver_button_exec; 
524         //op->poll= ??? // TODO: need to have some driver to be able to do this...
525         
526         /* flags */
527         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
528 }
529
530 /* Paste Driver Button Operator ------------------------ */
531
532 static int paste_driver_button_exec (bContext *C, wmOperator *op)
533 {
534         PointerRNA ptr;
535         PropertyRNA *prop= NULL;
536         char *path;
537         short success= 0;
538         int index;
539         
540         /* try to create driver using property retrieved from UI */
541         memset(&ptr, 0, sizeof(PointerRNA));
542         uiAnimContextProperty(C, &ptr, &prop, &index);
543         
544         if (ptr.data && prop && RNA_property_animateable(ptr.data, prop)) {
545                 path= RNA_path_from_ID_to_property(&ptr, prop);
546                 
547                 if (path) {
548                         /* only copy the driver for the button that this was involved for */
549                         success= ANIM_paste_driver(ptr.id.data, path, index, 0);
550                         
551                         MEM_freeN(path);
552                 }
553         }
554         
555         /* since we're just copying, we don't really need to do anything else...*/
556         return (success)? OPERATOR_FINISHED: OPERATOR_CANCELLED;
557 }
558
559 void ANIM_OT_paste_driver_button (wmOperatorType *ot)
560 {
561         /* identifiers */
562         ot->name= "Paste Driver";
563         ot->idname= "ANIM_OT_paste_driver_button";
564         ot->description= "Paste the driver in the copy/paste buffer for the highlighted button.";
565         
566         /* callbacks */
567         ot->exec= paste_driver_button_exec; 
568         //op->poll= ??? // TODO: need to have some driver to be able to do this...
569         
570         /* flags */
571         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
572 }
573
574
575 /* Copy to Clipboard Button Operator ------------------------ */
576
577 static int copy_clipboard_button_exec(bContext *C, wmOperator *op)
578 {
579         PointerRNA ptr;
580         PropertyRNA *prop= NULL;
581         char *path;
582         short success= 0;
583         int index;
584
585         /* try to create driver using property retrieved from UI */
586         memset(&ptr, 0, sizeof(PointerRNA));
587         uiAnimContextProperty(C, &ptr, &prop, &index);
588
589         if (ptr.data && prop) {
590                 path= RNA_path_from_ID_to_property(&ptr, prop);
591                 
592                 if (path) {
593                         WM_clipboard_text_set(path, FALSE);
594                         MEM_freeN(path);
595                 }
596         }
597
598         /* since we're just copying, we don't really need to do anything else...*/
599         return (success)? OPERATOR_FINISHED: OPERATOR_CANCELLED;
600 }
601
602 void ANIM_OT_copy_clipboard_button(wmOperatorType *ot)
603 {
604         /* identifiers */
605         ot->name= "Copy Data Path";
606         ot->idname= "ANIM_OT_copy_clipboard_button";
607         ot->description= "Copy the RNA data path for this property to the clipboard.";
608
609         /* callbacks */
610         ot->exec= copy_clipboard_button_exec;
611         //op->poll= ??? // TODO: need to have some valid property before this can be done
612
613         /* flags */
614         ot->flag= OPTYPE_REGISTER;
615 }
616
617 /* ************************************************** */