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