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