6ce9fc638be5f70dfb4e7362360c214074e757a7
[blender.git] / source / blender / editors / animation / anim_deps.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) 2008 Blender Foundation.
19  * All rights reserved.
20  *
21  * 
22  * Contributor(s): Blender Foundation, Joshua Leung
23  *
24  * ***** END GPL LICENSE BLOCK *****
25  */
26
27 /** \file blender/editors/animation/anim_deps.c
28  *  \ingroup edanimation
29  */
30
31
32 #include <string.h>
33
34 #include "MEM_guardedalloc.h"
35
36 #include "DNA_anim_types.h"
37 #include "DNA_armature_types.h"
38 #include "DNA_object_types.h"
39 #include "DNA_node_types.h"
40 #include "DNA_scene_types.h"
41 #include "DNA_sequence_types.h"
42
43 #include "BLI_blenlib.h"
44 #include "BLI_utildefines.h"
45
46 #include "BKE_animsys.h"
47 #include "BKE_action.h"
48 #include "BKE_context.h"
49 #include "BKE_depsgraph.h"
50 #include "BKE_global.h"
51 #include "BKE_node.h"
52 #include "BKE_sequencer.h"
53
54 #include "RNA_access.h"
55
56 #include "ED_anim_api.h"
57
58 /* **************************** depsgraph tagging ******************************** */
59
60 /* tags the given anim list element for refreshes (if applicable)
61  * due to Animation Editor editing 
62  */
63 void ANIM_list_elem_update(Scene *scene, bAnimListElem *ale)
64 {
65         ID *id;
66         FCurve *fcu;
67         AnimData *adt;
68
69         id = ale->id;
70         if (!id)
71                 return;
72         
73         /* tag AnimData for refresh so that other views will update in realtime with these changes */
74         adt = BKE_animdata_from_id(id);
75         if (adt)
76                 adt->recalc |= ADT_RECALC_ANIM;
77
78         /* update data */
79         fcu = (ale->datatype == ALE_FCURVE) ? ale->key_data : NULL;
80                 
81         if (fcu && fcu->rna_path) {
82                 /* if we have an fcurve, call the update for the property we
83                  * are editing, this is then expected to do the proper redraws
84                  * and depsgraph updates  */
85                 PointerRNA id_ptr, ptr;
86                 PropertyRNA *prop;
87                 
88                 RNA_id_pointer_create(id, &id_ptr);
89                         
90                 if (RNA_path_resolve(&id_ptr, fcu->rna_path, &ptr, &prop))
91                         RNA_property_update_main(G.main, scene, &ptr, prop);
92         }
93         else {
94                 /* in other case we do standard depsgaph update, ideally
95                  * we'd be calling property update functions here too ... */
96                 DAG_id_tag_update(id, OB_RECALC_OB | OB_RECALC_DATA | OB_RECALC_TIME); // XXX or do we want something more restrictive?
97         }
98 }
99
100 /* tags the given ID block for refreshes (if applicable) due to 
101  * Animation Editor editing */
102 void ANIM_id_update(Scene *UNUSED(scene), ID *id)
103 {
104         if (id) {
105                 AnimData *adt = BKE_animdata_from_id(id);
106                 
107                 /* tag AnimData for refresh so that other views will update in realtime with these changes */
108                 if (adt)
109                         adt->recalc |= ADT_RECALC_ANIM;
110                         
111                 /* set recalc flags */
112                 DAG_id_tag_update(id, OB_RECALC_OB | OB_RECALC_DATA | OB_RECALC_TIME); // XXX or do we want something more restrictive?
113         }
114 }
115
116 /* **************************** animation data <-> data syncing ******************************** */
117 /* This code here is used to synchronize the
118  *      - selection (to find selected data easier)
119  *      - ... (insert other relevant items here later) 
120  * status in relevant Blender data with the status stored in animation channels.
121  *
122  * This should be called in the refresh() callbacks for various editors in 
123  * response to appropriate notifiers.
124  */
125
126 /* perform syncing updates for Action Groups */
127 static void animchan_sync_group(bAnimContext *ac, bAnimListElem *ale, bActionGroup **active_agrp)
128 {
129         bActionGroup *agrp = (bActionGroup *)ale->data;
130         ID *owner_id = ale->id;
131         
132         /* major priority is selection status
133          * so we need both a group and an owner
134          */
135         if (ELEM(NULL, agrp, owner_id))
136                 return;
137                 
138         /* for standard Objects, check if group is the name of some bone */
139         if (GS(owner_id->name) == ID_OB) {
140                 Object *ob = (Object *)owner_id;
141                 
142                 /* check if there are bones, and whether the name matches any 
143                  * NOTE: this feature will only really work if groups by default contain the F-Curves for a single bone
144                  */
145                 // TODO: if bone gets renamed, it would be best to be able to rename the group
146                 if (ob->pose) {
147                         bPoseChannel *pchan = BKE_pose_channel_find_name(ob->pose, agrp->name);
148                         bArmature *arm = ob->data;
149                         
150                         if (pchan) {
151                                 bActionGroup *bgrp;
152                                 
153                                 /* if one matches, sync the selection status */
154                                 if ((pchan->bone) && (pchan->bone->flag & BONE_SELECTED))
155                                         agrp->flag |= AGRP_SELECTED;
156                                 else
157                                         agrp->flag &= ~AGRP_SELECTED;
158                                         
159                                 /* also sync active group status */
160                                 if ((ob == ac->obact) && (pchan->bone == arm->act_bone)) {
161                                         /* if no previous F-Curve has active flag, then we're the first and only one to get it */
162                                         if (*active_agrp == NULL) {
163                                                 agrp->flag |= AGRP_ACTIVE;
164                                                 *active_agrp = agrp;
165                                         }
166                                         else {
167                                                 /* someone else has already taken it - set as not active */
168                                                 agrp->flag &= ~AGRP_ACTIVE;
169                                         }
170                                 }
171                                 else {
172                                         /* this can't possibly be active now */
173                                         agrp->flag &= ~AGRP_ACTIVE;
174                                 }
175                                 
176                                 /* sync group colors */
177                                 bgrp = (bActionGroup *)BLI_findlink(&ob->pose->agroups, (pchan->agrp_index - 1));
178                                 if (bgrp) {
179                                         agrp->customCol = bgrp->customCol;
180                                         action_group_colors_sync(agrp, bgrp);
181                                 }
182                         }
183                 }
184         }
185 }
186  
187 /* perform syncing updates for F-Curves */
188 static void animchan_sync_fcurve(bAnimContext *ac, bAnimListElem *ale, FCurve **active_fcurve)
189 {
190         FCurve *fcu = (FCurve *)ale->data;
191         ID *owner_id = ale->id;
192         
193         /* major priority is selection status, so refer to the checks done in anim_filter.c 
194          * skip_fcurve_selected_data() for reference about what's going on here...
195          */
196         if (ELEM3(NULL, fcu, fcu->rna_path, owner_id))
197                 return;
198         
199         if (GS(owner_id->name) == ID_OB) {
200                 Object *ob = (Object *)owner_id;
201                 
202                 /* only affect if F-Curve involves pose.bones */
203                 if ((fcu->rna_path) && strstr(fcu->rna_path, "pose.bones")) {
204                         bArmature *arm = (bArmature *)ob->data;
205                         bPoseChannel *pchan;
206                         char *bone_name;
207                         
208                         /* get bone-name, and check if this bone is selected */
209                         bone_name = BLI_str_quoted_substrN(fcu->rna_path, "pose.bones[");
210                         pchan = BKE_pose_channel_find_name(ob->pose, bone_name);
211                         if (bone_name) MEM_freeN(bone_name);
212                         
213                         /* F-Curve selection depends on whether the bone is selected */
214                         if ((pchan) && (pchan->bone)) {
215                                 /* F-Curve selection */
216                                 if (pchan->bone->flag & BONE_SELECTED)
217                                         fcu->flag |= FCURVE_SELECTED;
218                                 else
219                                         fcu->flag &= ~FCURVE_SELECTED;
220                                         
221                                 /* Active F-Curve - it should be the first one for this bone on the 
222                                  * active object to be considered as active
223                                  */
224                                 if ((ob == ac->obact) && (pchan->bone == arm->act_bone)) {
225                                         /* if no previous F-Curve has active flag, then we're the first and only one to get it */
226                                         if (*active_fcurve == NULL) {
227                                                 fcu->flag |= FCURVE_ACTIVE;
228                                                 *active_fcurve = fcu;
229                                         }
230                                         else {
231                                                 /* someone else has already taken it - set as not active */
232                                                 fcu->flag &= ~FCURVE_ACTIVE;
233                                         }
234                                 }
235                                 else {
236                                         /* this can't possibly be active now */
237                                         fcu->flag &= ~FCURVE_ACTIVE;
238                                 }
239                         }
240                 }
241         }
242         else if (GS(owner_id->name) == ID_SCE) {
243                 Scene *scene = (Scene *)owner_id;
244                 
245                 /* only affect if F-Curve involves sequence_editor.sequences */
246                 if ((fcu->rna_path) && strstr(fcu->rna_path, "sequences_all")) {
247                         Editing *ed = BKE_sequencer_editing_get(scene, FALSE);
248                         Sequence *seq;
249                         char *seq_name;
250                         
251                         /* get strip name, and check if this strip is selected */
252                         seq_name = BLI_str_quoted_substrN(fcu->rna_path, "sequences_all[");
253                         seq = BKE_sequence_get_by_name(ed->seqbasep, seq_name, FALSE);
254                         if (seq_name) MEM_freeN(seq_name);
255                         
256                         /* update selection status */
257                         if (seq) {
258                                 if (seq->flag & SELECT)
259                                         fcu->flag |= FCURVE_SELECTED;
260                                 else
261                                         fcu->flag &= ~FCURVE_SELECTED;
262                         }
263                 }
264         }
265         else if (GS(owner_id->name) == ID_NT) {
266                 bNodeTree *ntree = (bNodeTree *)owner_id;
267                 
268                 /* check for selected nodes */
269                 if ((fcu->rna_path) && strstr(fcu->rna_path, "nodes")) {
270                         bNode *node;
271                         char *node_name;
272                         
273                         /* get strip name, and check if this strip is selected */
274                         node_name = BLI_str_quoted_substrN(fcu->rna_path, "nodes[");
275                         node = nodeFindNodebyName(ntree, node_name);
276                         if (node_name) MEM_freeN(node_name);
277                         
278                         /* update selection/active status */
279                         if (node) {
280                                 /* update selection status */
281                                 if (node->flag & NODE_SELECT)
282                                         fcu->flag |= FCURVE_SELECTED;
283                                 else
284                                         fcu->flag &= ~FCURVE_SELECTED;
285                                         
286                                 /* update active status */
287                                 /* XXX: this may interfere with setting bones as active if both exist at once;
288                                  * then again, if that's the case, production setups aren't likely to be animating
289                                  * nodes while working with bones?
290                                  */
291                                 if (node->flag & NODE_ACTIVE) {
292                                         if (*active_fcurve == NULL) {
293                                                 fcu->flag |= FCURVE_ACTIVE;
294                                                 *active_fcurve = fcu;
295                                         }
296                                         else {
297                                                 fcu->flag &= ~FCURVE_ACTIVE;
298                                         }
299                                 }
300                                 else {
301                                         fcu->flag &= ~FCURVE_ACTIVE;
302                                 }
303                         }
304                 }
305         }
306 }
307
308 /* ---------------- */
309  
310 /* Main call to be exported to animation editors */
311 void ANIM_sync_animchannels_to_data(const bContext *C)
312 {
313         bAnimContext ac;
314         ListBase anim_data = {NULL, NULL};
315         bAnimListElem *ale;
316         int filter;
317         
318         bActionGroup *active_agrp = NULL;
319         FCurve *active_fcurve = NULL;
320         
321         /* get animation context info for filtering the channels */
322         // TODO: check on whether we need to set the area specially instead, since active area might not be ok?
323         if (ANIM_animdata_get_context(C, &ac) == 0)
324                 return;
325         
326         /* filter data */
327         /* NOTE: we want all channels, since we want to be able to set selection status on some of them even when collapsed 
328          *       However, don't include duplicates so that selection statuses don't override each other
329          */
330         filter = ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_CHANNELS | ANIMFILTER_NODUPLIS;
331         ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
332         
333         /* flush settings as appropriate depending on the types of the channels */
334         for (ale = anim_data.first; ale; ale = ale->next) {
335                 switch (ale->type) {
336                         case ANIMTYPE_GROUP:
337                                 animchan_sync_group(&ac, ale, &active_agrp);
338                                 break;
339                         
340                         case ANIMTYPE_FCURVE:
341                                 animchan_sync_fcurve(&ac, ale, &active_fcurve);
342                                 break;
343                 }
344         }
345         
346         BLI_freelistN(&anim_data);
347 }