doxygen: prevent GPL license block from being parsed as doxygen comment.
[blender.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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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 <string.h>
32
33 #include "MEM_guardedalloc.h"
34
35 #include "BLI_blenlib.h"
36 #include "BLI_utildefines.h"
37
38 #include "DNA_anim_types.h"
39 #include "DNA_object_types.h"
40 #include "DNA_material_types.h"
41 #include "DNA_texture_types.h"
42 #include "DNA_screen_types.h"
43 #include "DNA_space_types.h"
44
45 #include "BKE_animsys.h"
46 #include "BKE_depsgraph.h"
47 #include "BKE_fcurve.h"
48 #include "BKE_context.h"
49 #include "BKE_report.h"
50 #include "BKE_material.h"
51 #include "BKE_texture.h"
52
53 #include "ED_keyframing.h"
54
55 #include "UI_interface.h"
56
57 #include "WM_api.h"
58 #include "WM_types.h"
59
60 #include "RNA_access.h"
61 #include "RNA_define.h"
62
63 #include "anim_intern.h"
64
65 /* called by WM */
66 void free_anim_drivers_copybuf (void);
67
68 /* ************************************************** */
69 /* Animation Data Validation */
70
71 /* Get (or add relevant data to be able to do so) F-Curve from the driver stack, 
72  * for the given Animation Data block. This assumes that all the destinations are valid.
73  *      
74  *      - add:  0 - don't add anything if not found, 
75  *                      1 - add new Driver FCurve, 
76  *                      -1 - add new Driver FCurve without driver stuff (for pasting)
77  */
78 static FCurve *verify_driver_fcurve (ID *id, const char rna_path[], const int array_index, short add)
79 {
80         AnimData *adt;
81         FCurve *fcu;
82         
83         /* sanity checks */
84         if ELEM(NULL, id, rna_path)
85                 return NULL;
86         
87         /* init animdata if none available yet */
88         adt= BKE_animdata_from_id(id);
89         if ((adt == NULL) && (add))
90                 adt= BKE_id_add_animdata(id);
91         if (adt == NULL) { 
92                 /* if still none (as not allowed to add, or ID doesn't have animdata for some reason) */
93                 return NULL;
94         }
95                 
96         /* try to find f-curve matching for this setting 
97          *      - add if not found and allowed to add one
98          *              TODO: add auto-grouping support? how this works will need to be resolved
99          */
100         fcu= list_find_fcurve(&adt->drivers, rna_path, array_index);
101         
102         if ((fcu == NULL) && (add)) {
103                 /* use default settings to make a F-Curve */
104                 fcu= MEM_callocN(sizeof(FCurve), "FCurve");
105                 
106                 fcu->flag = (FCURVE_VISIBLE|FCURVE_AUTO_HANDLES|FCURVE_SELECTED);
107                 
108                 /* store path - make copy, and store that */
109                 fcu->rna_path= BLI_strdupn(rna_path, strlen(rna_path));
110                 fcu->array_index= array_index;
111                 
112                 /* if add is negative, don't init this data yet, since it will be filled in by the pasted driver */
113                 if (add > 0) {
114                         /* add some new driver data */
115                         fcu->driver= MEM_callocN(sizeof(ChannelDriver), "ChannelDriver");
116                         
117                         /* add simple generator modifier for driver so that there is some visible representation */
118                         add_fmodifier(&fcu->modifiers, FMODIFIER_TYPE_GENERATOR);
119                 }
120                 
121                 /* just add F-Curve to end of driver list */
122                 BLI_addtail(&adt->drivers, fcu);
123         }
124         
125         /* return the F-Curve */
126         return fcu;
127 }
128
129 /* ************************************************** */
130 /* Driver Management API */
131
132 /* Main Driver Management API calls:
133  *      Add a new driver for the specified property on the given ID block
134  */
135 short ANIM_add_driver (ReportList *reports, ID *id, const char rna_path[], int array_index, short flag, int type)
136 {       
137         PointerRNA id_ptr, ptr;
138         PropertyRNA *prop;
139         FCurve *fcu;
140         int array_index_max;
141         int done = 0;
142         
143         /* validate pointer first - exit if failure */
144         RNA_id_pointer_create(id, &id_ptr);
145         if ((RNA_path_resolve(&id_ptr, rna_path, &ptr, &prop) == 0) || (prop == NULL)) {
146                 BKE_reportf(reports, RPT_ERROR, 
147                         "Could not add Driver, as RNA Path is invalid for the given ID (ID = %s, Path = %s)", 
148                         id->name, rna_path);
149                 return 0;
150         }
151         
152         /* key entire array convenience method */
153         if (array_index == -1) {
154                 array_index_max= RNA_property_array_length(&ptr, prop);
155                 array_index= 0;
156         }
157         else
158                 array_index_max= array_index;
159         
160         /* maximum index should be greater than the start index */
161         if (array_index == array_index_max)
162                 array_index_max += 1;
163         
164         /* will only loop once unless the array index was -1 */
165         for (; array_index < array_index_max; array_index++) {
166                 /* create F-Curve with Driver */
167                 fcu= verify_driver_fcurve(id, rna_path, array_index, 1);
168                 
169                 if (fcu && fcu->driver) {
170                         ChannelDriver *driver= fcu->driver;
171                         
172                         /* set the type of the driver */
173                         driver->type= type;
174                         
175                         /* creating drivers for buttons will create the driver(s) with type 
176                          * "scripted expression" so that their values won't be lost immediately,
177                          * so here we copy those values over to the driver's expression
178                          */
179                         if (type == DRIVER_TYPE_PYTHON) {
180                                 PropertyType proptype= RNA_property_type(prop);
181                                 int array= RNA_property_array_length(&ptr, prop);
182                                 char *expression= driver->expression;
183                                 int val, maxlen= sizeof(driver->expression);
184                                 float fval;
185                                 
186                                 if (proptype == PROP_BOOLEAN) {
187                                         if (!array) val= RNA_property_boolean_get(&ptr, prop);
188                                         else val= RNA_property_boolean_get_index(&ptr, prop, array_index);
189                                         
190                                         BLI_strncpy(expression, (val)? "True": "False", maxlen);
191                                 }
192                                 else if (proptype == PROP_INT) {
193                                         if (!array) val= RNA_property_int_get(&ptr, prop);
194                                         else val= RNA_property_int_get_index(&ptr, prop, array_index);
195                                         
196                                         BLI_snprintf(expression, maxlen, "%d", val);
197                                 }
198                                 else if (proptype == PROP_FLOAT) {
199                                         if (!array) fval= RNA_property_float_get(&ptr, prop);
200                                         else fval= RNA_property_float_get_index(&ptr, prop, array_index);
201                                         
202                                         BLI_snprintf(expression, maxlen, "%.3f", fval);
203                                 }
204                         }
205                         
206                         /* for easier setup of drivers from UI, a driver variable should be 
207                          * added if flag is set (UI calls only)
208                          */
209                         if (flag & CREATEDRIVER_WITH_DEFAULT_DVAR) {
210                                 /* assume that users will mostly want this to be of type "Transform Channel" too,
211                                  * since this allows the easiest setting up of common rig components
212                                  */
213                                 DriverVar *dvar = driver_add_new_variable(driver);
214                                 driver_change_variable_type(dvar, DVAR_TYPE_TRANSFORM_CHAN);
215                         }
216                 }
217                 
218                 /* set the done status */
219                 done += (fcu != NULL);
220         }
221         
222         /* done */
223         return done;
224 }
225
226 /* Main Driver Management API calls:
227  *      Remove the driver for the specified property on the given ID block (if available)
228  */
229 short ANIM_remove_driver (ReportList *UNUSED(reports), ID *id, const char rna_path[], int array_index, short UNUSED(flag))
230 {
231         AnimData *adt;
232         FCurve *fcu;
233         int success= 0;
234         
235         /* we don't check the validity of the path here yet, but it should be ok... */
236         adt= BKE_animdata_from_id(id);
237         
238         if (adt) {
239                 if (array_index == -1) {
240                         /* step through all drivers, removing all of those with the same base path */
241                         FCurve *fcu_iter= adt->drivers.first;
242                         
243                         while ((fcu = iter_step_fcurve(fcu_iter, rna_path)) != NULL) {
244                                 /* store the next fcurve for looping  */
245                                 fcu_iter= fcu->next;
246                                 
247                                 /* remove F-Curve from driver stack, then free it */
248                                 BLI_remlink(&adt->drivers, fcu);
249                                 free_fcurve(fcu);
250                                 
251                                 /* done successfully */
252                                 success |= 1;
253                         }
254                 }
255                 else {
256                         /* find the matching driver and remove it only 
257                          * Note: here is one of the places where we don't want new F-Curve + Driver added!
258                          *              so 'add' var must be 0
259                          */
260                         fcu= verify_driver_fcurve(id, rna_path, array_index, 0);
261                         if (fcu) {
262                                 BLI_remlink(&adt->drivers, fcu);
263                                 free_fcurve(fcu);
264                                 
265                                 success = 1;
266                         }
267                 }
268         }
269
270         return success;
271 }
272
273 /* ************************************************** */
274 /* Driver Management API - Copy/Paste Drivers */
275
276 /* Copy/Paste Buffer for Driver Data... */
277 static FCurve *channeldriver_copypaste_buf = NULL;
278
279 /* This function frees any MEM_calloc'ed copy/paste buffer data */
280 // XXX find some header to put this in!
281 void free_anim_drivers_copybuf (void)
282 {
283         /* free the buffer F-Curve if it exists, as if it were just another F-Curve */
284         if (channeldriver_copypaste_buf)
285                 free_fcurve(channeldriver_copypaste_buf);
286         channeldriver_copypaste_buf= NULL;
287 }
288
289 /* Checks if there is a driver in the copy/paste buffer */
290 short ANIM_driver_can_paste (void)
291 {
292         return (channeldriver_copypaste_buf != NULL);
293 }
294
295 /* ------------------- */
296
297 /* Main Driver Management API calls:
298  *      Make a copy of the driver for the specified property on the given ID block
299  */
300 short ANIM_copy_driver (ReportList *reports, ID *id, const char rna_path[], int array_index, short UNUSED(flag))
301 {
302         PointerRNA id_ptr, ptr;
303         PropertyRNA *prop;
304         FCurve *fcu;
305         
306         /* validate pointer first - exit if failure */
307         RNA_id_pointer_create(id, &id_ptr);
308         if ((RNA_path_resolve(&id_ptr, rna_path, &ptr, &prop) == 0) || (prop == NULL)) {
309                 BKE_reportf(reports, RPT_ERROR,
310                         "Could not find Driver to copy, as RNA Path is invalid for the given ID (ID = %s, Path = %s)", 
311                         id->name, rna_path);
312                 return 0;
313         }
314         
315         /* try to get F-Curve with Driver */
316         fcu= verify_driver_fcurve(id, rna_path, array_index, 0);
317         
318         /* clear copy/paste buffer first (for consistency with other copy/paste buffers) */
319         free_anim_drivers_copybuf();
320         
321         /* copy this to the copy/paste buf if it exists */
322         if (fcu && fcu->driver) {
323                 /* make copies of some info such as the rna_path, then clear this info from the F-Curve temporarily
324                  * so that we don't end up wasting memory storing the path which won't get used ever...
325                  */
326                 char *tmp_path = fcu->rna_path;
327                 fcu->rna_path= NULL;
328                 
329                 /* make a copy of the F-Curve with */
330                 channeldriver_copypaste_buf= copy_fcurve(fcu);
331                 
332                 /* restore the path */
333                 fcu->rna_path= tmp_path;
334                 
335                 /* copied... */
336                 return 1;
337         }
338         
339         /* done */
340         return 0;
341 }
342
343 /* Main Driver Management API calls:
344  *      Add a new driver for the specified property on the given ID block or replace an existing one
345  *      with the driver + driver-curve data from the buffer 
346  */
347 short ANIM_paste_driver (ReportList *reports, ID *id, const char rna_path[], int array_index, short UNUSED(flag))
348 {       
349         PointerRNA id_ptr, ptr;
350         PropertyRNA *prop;
351         FCurve *fcu;
352         
353         /* validate pointer first - exit if failure */
354         RNA_id_pointer_create(id, &id_ptr);
355         if ((RNA_path_resolve(&id_ptr, rna_path, &ptr, &prop) == 0) || (prop == NULL)) {
356                 BKE_reportf(reports, RPT_ERROR,
357                         "Could not paste Driver, as RNA Path is invalid for the given ID (ID = %s, Path = %s)", 
358                         id->name, rna_path);
359                 return 0;
360         }
361         
362         /* if the buffer is empty, cannot paste... */
363         if (channeldriver_copypaste_buf == NULL) {
364                 BKE_report(reports, RPT_ERROR, "Paste Driver: No Driver to paste.");
365                 return 0;
366         }
367         
368         /* create Driver F-Curve, but without data which will be copied across... */
369         fcu= verify_driver_fcurve(id, rna_path, array_index, -1);
370
371         if (fcu) {
372                 /* copy across the curve data from the buffer curve 
373                  * NOTE: this step needs care to not miss new settings
374                  */
375                         /* keyframes/samples */
376                 fcu->bezt= MEM_dupallocN(channeldriver_copypaste_buf->bezt);
377                 fcu->fpt= MEM_dupallocN(channeldriver_copypaste_buf->fpt);
378                 fcu->totvert= channeldriver_copypaste_buf->totvert;
379                 
380                         /* modifiers */
381                 copy_fmodifiers(&fcu->modifiers, &channeldriver_copypaste_buf->modifiers);
382                 
383                         /* flags - on a per-relevant-flag basis */
384                 if (channeldriver_copypaste_buf->flag & FCURVE_AUTO_HANDLES)
385                         fcu->flag |= FCURVE_AUTO_HANDLES;
386                 else
387                         fcu->flag &= ~FCURVE_AUTO_HANDLES;
388                         /* extrapolation mode */
389                 fcu->extend= channeldriver_copypaste_buf->extend;
390                         
391                         /* the 'juicy' stuff - the driver */
392                 fcu->driver= fcurve_copy_driver(channeldriver_copypaste_buf->driver);
393         }
394         
395         /* done */
396         return (fcu != NULL);
397 }
398
399 /* ************************************************** */
400 /* UI-Button Interface */
401
402 /* Temporary wrapper for driver operators for buttons to make it easier to create
403  * such drivers by rerouting all paths through the active object instead so that
404  * they will get picked up by the dependency system.
405  *
406  * < C: context pointer - for getting active data 
407  * <> ptr: RNA pointer for property's datablock. May be modified as result of path remapping.
408  * < prop: RNA definition of property to add for
409  *
410  * > returns: MEM_alloc'd string representing the path to the property from the given PointerRNA
411  */
412 static char *get_driver_path_hack (bContext *C, PointerRNA *ptr, PropertyRNA *prop)
413 {
414         ID *id = (ID *)ptr->id.data;
415         ScrArea *sa = CTX_wm_area(C);
416         
417         /* get standard path which may be extended */
418         char *basepath = RNA_path_from_ID_to_property(ptr, prop);
419         char *path = basepath; /* in case no remapping is needed */
420         
421         /* Remapping will only be performed in the Properties Editor, as only this 
422          * restricts the subspace of options to the 'active' data (a manageable state)
423          */
424         // TODO: watch out for pinned context?
425         if ((sa) && (sa->spacetype == SPACE_BUTS)) {
426                 Object *ob = CTX_data_active_object(C);
427                 
428                 if (ob && id) {
429                         /* only id-types which can be remapped to go through objects should be considered */
430                         switch (GS(id->name)) {
431                                 case ID_MA: /* materials */
432                                 {
433                                         Material *ma = give_current_material(ob, ob->actcol);
434                                         
435                                         /* assumes: material will only be shown if it is active objects's active material it's ok */
436                                         if ((ID*)ma == id) {
437                                                 /* create new path */
438                                                 // TODO: use RNA path functions to construct instead?
439                                                 path = BLI_sprintfN("material_slots[\"%s\"].material.%s",
440                                                         ma->id.name+2, basepath);
441                                                         
442                                                 /* free old one */
443                                                 MEM_freeN(basepath);
444                                         }
445                                 }
446                                         break;
447                                         
448                                 case ID_TE: /* textures */
449                                 {
450                                         Material *ma = give_current_material(ob, ob->actcol);
451                                         Tex *tex = give_current_material_texture(ma);
452                                         
453                                         /* assumes: texture will only be shown if it is active material's active texture it's ok */
454                                         if ((ID*)tex == id) {
455                                                 /* create new path */
456                                                 // TODO: use RNA path functions to construct step by step instead?
457                                                 path = BLI_sprintfN("material_slots[\"%s\"].material.texture_slots[\"%s\"].texture.%s", 
458                                                         ma->id.name+2, tex->id.name+2, basepath);
459                                                         
460                                                 /* free old one */
461                                                 MEM_freeN(basepath);
462                                         }
463                                 }
464                                         break;
465                         }
466                         
467                         /* fix RNA pointer, as we've now changed the ID root by changing the paths */
468                         if (basepath != path) {
469                                 /* rebase provided pointer so that it starts from object... */
470                                 RNA_pointer_create(&ob->id, ptr->type, ptr->data, ptr);
471                         }
472                 }
473         }
474         
475         /* the path should now have been corrected for use */
476         return path;
477 }
478
479 /* Add Driver Button Operator ------------------------ */
480
481 static int add_driver_button_exec (bContext *C, wmOperator *op)
482 {
483         PointerRNA ptr= {{NULL}};
484         PropertyRNA *prop= NULL;
485         short success= 0;
486         int index, all= RNA_boolean_get(op->ptr, "all");
487         
488         /* try to create driver using property retrieved from UI */
489         uiContextActiveProperty(C, &ptr, &prop, &index);
490         
491         if (all)
492                 index= -1;
493         
494         if (ptr.id.data && ptr.data && prop && RNA_property_animateable(&ptr, prop)) {
495                 char *path= get_driver_path_hack(C, &ptr, prop);
496                 short flags = CREATEDRIVER_WITH_DEFAULT_DVAR;
497                 
498                 if (path) {                     
499                         success+= ANIM_add_driver(op->reports, ptr.id.data, path, index, flags, DRIVER_TYPE_PYTHON);
500                         
501                         MEM_freeN(path);
502                 }
503         }
504         
505         if (success) {
506                 /* send updates */
507                 uiContextAnimUpdate(C);
508                 
509                 DAG_ids_flush_update(CTX_data_main(C), 0);
510                 
511                 WM_event_add_notifier(C, NC_ANIMATION|ND_FCURVES_ORDER, NULL); // XXX
512         }
513         
514         return (success)? OPERATOR_FINISHED: OPERATOR_CANCELLED;
515 }
516
517 void ANIM_OT_driver_button_add (wmOperatorType *ot)
518 {
519         /* identifiers */
520         ot->name= "Add Driver";
521         ot->idname= "ANIM_OT_driver_button_add";
522         ot->description= "Add driver(s) for the property(s) connected represented by the highlighted button";
523         
524         /* callbacks */
525         ot->exec= add_driver_button_exec; 
526         //op->poll= ??? // TODO: need to have some animateable property to do this
527         
528         /* flags */
529         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
530
531         /* properties */
532         RNA_def_boolean(ot->srna, "all", 1, "All", "Create drivers for all elements of the array.");
533 }
534
535 /* Remove Driver Button Operator ------------------------ */
536
537 static int remove_driver_button_exec (bContext *C, wmOperator *op)
538 {
539         PointerRNA ptr= {{NULL}};
540         PropertyRNA *prop= NULL;
541         short success= 0;
542         int index, all= RNA_boolean_get(op->ptr, "all");
543         
544         /* try to find driver using property retrieved from UI */
545         uiContextActiveProperty(C, &ptr, &prop, &index);
546         
547         if (all)
548                 index= -1;
549         
550         if (ptr.id.data && ptr.data && prop) {
551                 char *path= get_driver_path_hack(C, &ptr, prop);
552                 
553                 success= ANIM_remove_driver(op->reports, ptr.id.data, path, index, 0);
554                 MEM_freeN(path);
555         }
556         
557         if (success) {
558                 /* send updates */
559                 uiContextAnimUpdate(C);
560                 
561                 DAG_ids_flush_update(CTX_data_main(C), 0);
562                 
563                 WM_event_add_notifier(C, NC_ANIMATION|ND_FCURVES_ORDER, NULL);  // XXX
564         }
565         
566         return (success)? OPERATOR_FINISHED: OPERATOR_CANCELLED;
567 }
568
569 void ANIM_OT_driver_button_remove (wmOperatorType *ot)
570 {
571         /* identifiers */
572         ot->name= "Remove Driver";
573         ot->idname= "ANIM_OT_driver_button_remove";
574         ot->description= "Remove the driver(s) for the property(s) connected represented by the highlighted button";
575         
576         /* callbacks */
577         ot->exec= remove_driver_button_exec; 
578         //op->poll= ??? // TODO: need to have some driver to be able to do this...
579         
580         /* flags */
581         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
582
583         /* properties */
584         RNA_def_boolean(ot->srna, "all", 1, "All", "Delete drivers for all elements of the array.");
585 }
586
587 /* Copy Driver Button Operator ------------------------ */
588
589 static int copy_driver_button_exec (bContext *C, wmOperator *op)
590 {
591         PointerRNA ptr= {{NULL}};
592         PropertyRNA *prop= NULL;
593         short success= 0;
594         int index;
595         
596         /* try to create driver using property retrieved from UI */
597         uiContextActiveProperty(C, &ptr, &prop, &index);
598         
599         if (ptr.id.data && ptr.data && prop && RNA_property_animateable(&ptr, prop)) {
600                 char *path= get_driver_path_hack(C, &ptr, prop);
601                 
602                 if (path) {
603                         /* only copy the driver for the button that this was involved for */
604                         success= ANIM_copy_driver(op->reports, ptr.id.data, path, index, 0);
605                         
606                         uiContextAnimUpdate(C);
607                         
608                         MEM_freeN(path);
609                 }
610         }
611         
612         /* since we're just copying, we don't really need to do anything else...*/
613         return (success)? OPERATOR_FINISHED: OPERATOR_CANCELLED;
614 }
615
616 void ANIM_OT_copy_driver_button (wmOperatorType *ot)
617 {
618         /* identifiers */
619         ot->name= "Copy Driver";
620         ot->idname= "ANIM_OT_copy_driver_button";
621         ot->description= "Copy the driver for the highlighted button";
622         
623         /* callbacks */
624         ot->exec= copy_driver_button_exec; 
625         //op->poll= ??? // TODO: need to have some driver to be able to do this...
626         
627         /* flags */
628         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
629 }
630
631 /* Paste Driver Button Operator ------------------------ */
632
633 static int paste_driver_button_exec (bContext *C, wmOperator *op)
634 {
635         PointerRNA ptr= {{NULL}};
636         PropertyRNA *prop= NULL;
637         short success= 0;
638         int index;
639         
640         /* try to create driver using property retrieved from UI */
641         uiContextActiveProperty(C, &ptr, &prop, &index);
642         
643         if (ptr.id.data && ptr.data && prop && RNA_property_animateable(&ptr, prop)) {
644                 char *path= get_driver_path_hack(C, &ptr, prop);
645                 
646                 if (path) {
647                         /* only copy the driver for the button that this was involved for */
648                         success= ANIM_paste_driver(op->reports, ptr.id.data, path, index, 0);
649                         
650                         uiContextAnimUpdate(C);
651                         
652                         MEM_freeN(path);
653                 }
654         }
655         
656         /* since we're just copying, we don't really need to do anything else...*/
657         return (success)? OPERATOR_FINISHED: OPERATOR_CANCELLED;
658 }
659
660 void ANIM_OT_paste_driver_button (wmOperatorType *ot)
661 {
662         /* identifiers */
663         ot->name= "Paste Driver";
664         ot->idname= "ANIM_OT_paste_driver_button";
665         ot->description= "Paste the driver in the copy/paste buffer for the highlighted button";
666         
667         /* callbacks */
668         ot->exec= paste_driver_button_exec; 
669         //op->poll= ??? // TODO: need to have some driver to be able to do this...
670         
671         /* flags */
672         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
673 }
674
675 /* ************************************************** */