1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  */
16 
17 /** \file
18  * \ingroup edinterface
19  */
20 
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 
25 #include "MEM_guardedalloc.h"
26 
27 #include "DNA_anim_types.h"
28 #include "DNA_scene_types.h"
29 #include "DNA_screen_types.h"
30 
31 #include "BLI_listbase.h"
32 #include "BLI_string.h"
33 #include "BLI_string_utf8.h"
34 #include "BLI_utildefines.h"
35 
36 #include "BKE_animsys.h"
37 #include "BKE_context.h"
38 #include "BKE_fcurve.h"
39 #include "BKE_fcurve_driver.h"
40 #include "BKE_global.h"
41 #include "BKE_main.h"
42 #include "BKE_nla.h"
43 
44 #include "DEG_depsgraph.h"
45 #include "DEG_depsgraph_build.h"
46 
47 #include "ED_keyframing.h"
48 
49 #include "UI_interface.h"
50 
51 #include "RNA_access.h"
52 
53 #include "WM_api.h"
54 #include "WM_types.h"
55 
56 #include "interface_intern.h"
57 
ui_but_get_fcurve(uiBut * but,AnimData ** adt,bAction ** action,bool * r_driven,bool * r_special)58 static FCurve *ui_but_get_fcurve(
59     uiBut *but, AnimData **adt, bAction **action, bool *r_driven, bool *r_special)
60 {
61   /* for entire array buttons we check the first component, it's not perfect
62    * but works well enough in typical cases */
63   const int rnaindex = (but->rnaindex == -1) ? 0 : but->rnaindex;
64 
65   return BKE_fcurve_find_by_rna_context_ui(
66       but->block->evil_C, &but->rnapoin, but->rnaprop, rnaindex, adt, action, r_driven, r_special);
67 }
68 
ui_but_anim_flag(uiBut * but,const AnimationEvalContext * anim_eval_context)69 void ui_but_anim_flag(uiBut *but, const AnimationEvalContext *anim_eval_context)
70 {
71   AnimData *adt;
72   bAction *act;
73   FCurve *fcu;
74   bool driven;
75   bool special;
76 
77   but->flag &= ~(UI_BUT_ANIMATED | UI_BUT_ANIMATED_KEY | UI_BUT_DRIVEN);
78   but->drawflag &= ~UI_BUT_ANIMATED_CHANGED;
79 
80   /* NOTE: "special" is reserved for special F-Curves stored on the animation data
81    *        itself (which are used to animate properties of the animation data).
82    *        We count those as "animated" too for now
83    */
84   fcu = ui_but_get_fcurve(but, &adt, &act, &driven, &special);
85 
86   if (fcu) {
87     if (!driven) {
88       /* Empty curves are ignored by the animation evaluation system. */
89       if (BKE_fcurve_is_empty(fcu)) {
90         return;
91       }
92 
93       but->flag |= UI_BUT_ANIMATED;
94 
95       /* T41525 - When the active action is a NLA strip being edited,
96        * we need to correct the frame number to "look inside" the
97        * remapped action
98        */
99       float cfra = anim_eval_context->eval_time;
100       if (adt) {
101         cfra = BKE_nla_tweakedit_remap(adt, cfra, NLATIME_CONVERT_UNMAP);
102       }
103 
104       if (fcurve_frame_has_keyframe(fcu, cfra, 0)) {
105         but->flag |= UI_BUT_ANIMATED_KEY;
106       }
107 
108       /* XXX: this feature is totally broken and useless with NLA */
109       if (adt == NULL || adt->nla_tracks.first == NULL) {
110         const AnimationEvalContext remapped_context = BKE_animsys_eval_context_construct_at(
111             anim_eval_context, cfra);
112         if (fcurve_is_changed(but->rnapoin, but->rnaprop, fcu, &remapped_context)) {
113           but->drawflag |= UI_BUT_ANIMATED_CHANGED;
114         }
115       }
116     }
117     else {
118       but->flag |= UI_BUT_DRIVEN;
119     }
120   }
121 }
122 
ui_but_anim_decorate_find_attached_button(uiButDecorator * but_decorate)123 static uiBut *ui_but_anim_decorate_find_attached_button(uiButDecorator *but_decorate)
124 {
125   uiBut *but_iter = NULL;
126 
127   BLI_assert(UI_but_is_decorator(&but_decorate->but));
128   BLI_assert(but_decorate->rnapoin.data && but_decorate->rnaprop);
129 
130   LISTBASE_CIRCULAR_BACKWARD_BEGIN (
131       &but_decorate->but.block->buttons, but_iter, but_decorate->but.prev) {
132     if (but_iter != (uiBut *)but_decorate &&
133         ui_but_rna_equals_ex(
134             but_iter, &but_decorate->rnapoin, but_decorate->rnaprop, but_decorate->rnaindex)) {
135       return but_iter;
136     }
137   }
138   LISTBASE_CIRCULAR_BACKWARD_END(
139       &but_decorate->but.block->buttons, but_iter, but_decorate->but.prev);
140 
141   return NULL;
142 }
143 
ui_but_anim_decorate_update_from_flag(uiButDecorator * decorator_but)144 void ui_but_anim_decorate_update_from_flag(uiButDecorator *decorator_but)
145 {
146   if (!decorator_but->rnapoin.data || !decorator_but->rnaprop) {
147     /* Nothing to do. */
148     return;
149   }
150 
151   const uiBut *but_anim = ui_but_anim_decorate_find_attached_button(decorator_but);
152   uiBut *but = &decorator_but->but;
153 
154   if (!but_anim) {
155     printf("Could not find button with matching property to decorate (%s.%s)\n",
156            RNA_struct_identifier(decorator_but->rnapoin.type),
157            RNA_property_identifier(decorator_but->rnaprop));
158     return;
159   }
160 
161   const int flag = but_anim->flag;
162 
163   if (flag & UI_BUT_DRIVEN) {
164     but->icon = ICON_DECORATE_DRIVER;
165   }
166   else if (flag & UI_BUT_ANIMATED_KEY) {
167     but->icon = ICON_DECORATE_KEYFRAME;
168   }
169   else if (flag & UI_BUT_ANIMATED) {
170     but->icon = ICON_DECORATE_ANIMATE;
171   }
172   else if (flag & UI_BUT_OVERRIDEN) {
173     but->icon = ICON_DECORATE_OVERRIDE;
174   }
175   else {
176     but->icon = ICON_DECORATE;
177   }
178 
179   const int flag_copy = (UI_BUT_DISABLED | UI_BUT_INACTIVE);
180   but->flag = (but->flag & ~flag_copy) | (flag & flag_copy);
181 }
182 
183 /**
184  * \a str can be NULL to only perform check if \a but has an expression at all.
185  * \return if button has an expression.
186  */
ui_but_anim_expression_get(uiBut * but,char * str,size_t maxlen)187 bool ui_but_anim_expression_get(uiBut *but, char *str, size_t maxlen)
188 {
189   FCurve *fcu;
190   ChannelDriver *driver;
191   bool driven, special;
192 
193   fcu = ui_but_get_fcurve(but, NULL, NULL, &driven, &special);
194 
195   if (fcu && driven) {
196     driver = fcu->driver;
197 
198     if (driver && driver->type == DRIVER_TYPE_PYTHON) {
199       if (str) {
200         BLI_strncpy(str, driver->expression, maxlen);
201       }
202       return true;
203     }
204   }
205 
206   return false;
207 }
208 
ui_but_anim_expression_set(uiBut * but,const char * str)209 bool ui_but_anim_expression_set(uiBut *but, const char *str)
210 {
211   FCurve *fcu;
212   ChannelDriver *driver;
213   bool driven, special;
214 
215   fcu = ui_but_get_fcurve(but, NULL, NULL, &driven, &special);
216 
217   if (fcu && driven) {
218     driver = fcu->driver;
219 
220     if (driver && (driver->type == DRIVER_TYPE_PYTHON)) {
221       bContext *C = but->block->evil_C;
222 
223       BLI_strncpy_utf8(driver->expression, str, sizeof(driver->expression));
224 
225       /* tag driver as needing to be recompiled */
226       BKE_driver_invalidate_expression(driver, true, false);
227 
228       /* clear invalid flags which may prevent this from working */
229       driver->flag &= ~DRIVER_FLAG_INVALID;
230       fcu->flag &= ~FCURVE_DISABLED;
231 
232       /* this notifier should update the Graph Editor and trigger depsgraph refresh? */
233       WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME, NULL);
234 
235       DEG_relations_tag_update(CTX_data_main(C));
236 
237       return true;
238     }
239   }
240 
241   return false;
242 }
243 
244 /* create new expression for button (i.e. a "scripted driver"), if it can be created... */
ui_but_anim_expression_create(uiBut * but,const char * str)245 bool ui_but_anim_expression_create(uiBut *but, const char *str)
246 {
247   bContext *C = but->block->evil_C;
248   ID *id;
249   FCurve *fcu;
250   char *path;
251   bool ok = false;
252 
253   /* button must have RNA-pointer to a numeric-capable property */
254   if (ELEM(NULL, but->rnapoin.data, but->rnaprop)) {
255     if (G.debug & G_DEBUG) {
256       printf("ERROR: create expression failed - button has no RNA info attached\n");
257     }
258     return false;
259   }
260 
261   if (RNA_property_array_check(but->rnaprop) != 0) {
262     if (but->rnaindex == -1) {
263       if (G.debug & G_DEBUG) {
264         printf("ERROR: create expression failed - can't create expression for entire array\n");
265       }
266       return false;
267     }
268   }
269 
270   /* make sure we have animdata for this */
271   /* FIXME: until materials can be handled by depsgraph,
272    * don't allow drivers to be created for them */
273   id = but->rnapoin.owner_id;
274   if ((id == NULL) || (GS(id->name) == ID_MA) || (GS(id->name) == ID_TE)) {
275     if (G.debug & G_DEBUG) {
276       printf("ERROR: create expression failed - invalid data-block for adding drivers (%p)\n", id);
277     }
278     return false;
279   }
280 
281   /* get path */
282   path = RNA_path_from_ID_to_property(&but->rnapoin, but->rnaprop);
283   if (path == NULL) {
284     return false;
285   }
286 
287   /* create driver */
288   fcu = verify_driver_fcurve(id, path, but->rnaindex, DRIVER_FCURVE_KEYFRAMES);
289   if (fcu) {
290     ChannelDriver *driver = fcu->driver;
291 
292     if (driver) {
293       /* set type of driver */
294       driver->type = DRIVER_TYPE_PYTHON;
295 
296       /* set the expression */
297       /* TODO: need some way of identifying variables used */
298       BLI_strncpy_utf8(driver->expression, str, sizeof(driver->expression));
299 
300       /* updates */
301       BKE_driver_invalidate_expression(driver, true, false);
302       DEG_relations_tag_update(CTX_data_main(C));
303       WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME, NULL);
304       ok = true;
305     }
306   }
307 
308   MEM_freeN(path);
309 
310   return ok;
311 }
312 
ui_but_anim_autokey(bContext * C,uiBut * but,Scene * scene,float cfra)313 void ui_but_anim_autokey(bContext *C, uiBut *but, Scene *scene, float cfra)
314 {
315   ED_autokeyframe_property(C, scene, &but->rnapoin, but->rnaprop, but->rnaindex, cfra);
316 }
317 
ui_but_anim_copy_driver(bContext * C)318 void ui_but_anim_copy_driver(bContext *C)
319 {
320   /* this operator calls UI_context_active_but_prop_get */
321   WM_operator_name_call(C, "ANIM_OT_copy_driver_button", WM_OP_INVOKE_DEFAULT, NULL);
322 }
323 
ui_but_anim_paste_driver(bContext * C)324 void ui_but_anim_paste_driver(bContext *C)
325 {
326   /* this operator calls UI_context_active_but_prop_get */
327   WM_operator_name_call(C, "ANIM_OT_paste_driver_button", WM_OP_INVOKE_DEFAULT, NULL);
328 }
329 
ui_but_anim_decorate_cb(bContext * C,void * arg_but,void * UNUSED (arg_dummy))330 void ui_but_anim_decorate_cb(bContext *C, void *arg_but, void *UNUSED(arg_dummy))
331 {
332   wmWindowManager *wm = CTX_wm_manager(C);
333   uiButDecorator *but_decorate = arg_but;
334   uiBut *but_anim = ui_but_anim_decorate_find_attached_button(but_decorate);
335 
336   if (!but_anim) {
337     return;
338   }
339 
340   /* FIXME(campbell), swapping active pointer is weak. */
341   SWAP(struct uiHandleButtonData *, but_anim->active, but_decorate->but.active);
342   wm->op_undo_depth++;
343 
344   if (but_anim->flag & UI_BUT_DRIVEN) {
345     /* pass */
346     /* TODO: report? */
347   }
348   else if (but_anim->flag & UI_BUT_ANIMATED_KEY) {
349     PointerRNA props_ptr;
350     wmOperatorType *ot = WM_operatortype_find("ANIM_OT_keyframe_delete_button", false);
351     WM_operator_properties_create_ptr(&props_ptr, ot);
352     RNA_boolean_set(&props_ptr, "all", but_anim->rnaindex == -1);
353     WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &props_ptr);
354     WM_operator_properties_free(&props_ptr);
355   }
356   else {
357     PointerRNA props_ptr;
358     wmOperatorType *ot = WM_operatortype_find("ANIM_OT_keyframe_insert_button", false);
359     WM_operator_properties_create_ptr(&props_ptr, ot);
360     RNA_boolean_set(&props_ptr, "all", but_anim->rnaindex == -1);
361     WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &props_ptr);
362     WM_operator_properties_free(&props_ptr);
363   }
364 
365   SWAP(struct uiHandleButtonData *, but_anim->active, but_decorate->but.active);
366   wm->op_undo_depth--;
367 }
368