FCurves: Remember active fcurve when selecting other bone
[blender.git] / source / blender / editors / animation / anim_deps.c
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  * The Original Code is Copyright (C) 2008 Blender Foundation.
17  * All rights reserved.
18  */
19
20 /** \file
21  * \ingroup edanimation
22  */
23
24
25 #include <string.h>
26
27 #include "MEM_guardedalloc.h"
28
29 #include "DNA_anim_types.h"
30 #include "DNA_armature_types.h"
31 #include "DNA_gpencil_types.h"
32 #include "DNA_object_types.h"
33 #include "DNA_node_types.h"
34 #include "DNA_scene_types.h"
35 #include "DNA_sequence_types.h"
36
37 #include "BLI_blenlib.h"
38 #include "BLI_utildefines.h"
39
40 #include "BKE_animsys.h"
41 #include "BKE_action.h"
42 #include "BKE_context.h"
43 #include "BKE_fcurve.h"
44 #include "BKE_gpencil.h"
45 #include "BKE_main.h"
46 #include "BKE_node.h"
47 #include "BKE_sequencer.h"
48
49 #include "DEG_depsgraph.h"
50
51 #include "RNA_access.h"
52
53 #include "ED_anim_api.h"
54
55 /* **************************** depsgraph tagging ******************************** */
56
57 /* tags the given anim list element for refreshes (if applicable)
58  * due to Animation Editor editing
59  */
60 void ANIM_list_elem_update(Main *bmain, Scene *scene, bAnimListElem *ale)
61 {
62         ID *id;
63         FCurve *fcu;
64         AnimData *adt;
65
66         id = ale->id;
67         if (!id)
68                 return;
69
70         /* tag AnimData for refresh so that other views will update in realtime with these changes */
71         adt = BKE_animdata_from_id(id);
72         if (adt) {
73                 DEG_id_tag_update(id, ID_RECALC_ANIMATION);
74                 if (adt->action != NULL) {
75                         DEG_id_tag_update(&adt->action->id, ID_RECALC_COPY_ON_WRITE);
76                 }
77         }
78
79         /* Tag copy on the main object if updating anything directly inside AnimData */
80         if (ELEM(ale->type, ANIMTYPE_ANIMDATA, ANIMTYPE_NLAACTION, ANIMTYPE_NLATRACK, ANIMTYPE_NLACURVE)) {
81                 DEG_id_tag_update(id, ID_RECALC_ANIMATION | ID_RECALC_COPY_ON_WRITE);
82                 return;
83         }
84
85         /* update data */
86         fcu = (ale->datatype == ALE_FCURVE) ? ale->key_data : NULL;
87
88         if (fcu && fcu->rna_path) {
89                 /* if we have an fcurve, call the update for the property we
90                  * are editing, this is then expected to do the proper redraws
91                  * and depsgraph updates  */
92                 PointerRNA id_ptr, ptr;
93                 PropertyRNA *prop;
94
95                 RNA_id_pointer_create(id, &id_ptr);
96
97                 if (RNA_path_resolve_property(&id_ptr, fcu->rna_path, &ptr, &prop))
98                         RNA_property_update_main(bmain, scene, &ptr, prop);
99         }
100         else {
101                 /* in other case we do standard depsgraph update, ideally
102                  * we'd be calling property update functions here too ... */
103                 DEG_id_tag_update(id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY | ID_RECALC_ANIMATION); // XXX or do we want something more restrictive?
104         }
105 }
106
107 /* tags the given ID block for refreshes (if applicable) due to
108  * Animation Editor editing */
109 void ANIM_id_update(Main *bmain, ID *id)
110 {
111         if (id) {
112                 DEG_id_tag_update_ex(bmain, id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY | ID_RECALC_ANIMATION); // 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                 if (ob->pose) {
146                         bPoseChannel *pchan = BKE_pose_channel_find_name(ob->pose, agrp->name);
147                         bArmature *arm = ob->data;
148
149                         if (pchan) {
150                                 bActionGroup *bgrp;
151
152                                 /* if one matches, sync the selection status */
153                                 if ((pchan->bone) && (pchan->bone->flag & BONE_SELECTED))
154                                         agrp->flag |= AGRP_SELECTED;
155                                 else
156                                         agrp->flag &= ~AGRP_SELECTED;
157
158                                 /* also sync active group status */
159                                 if ((ob == ac->obact) && (pchan->bone == arm->act_bone)) {
160                                         /* if no previous F-Curve has active flag, then we're the first and only one to get it */
161                                         if (*active_agrp == NULL) {
162                                                 agrp->flag |= AGRP_ACTIVE;
163                                                 *active_agrp = agrp;
164                                         }
165                                         else {
166                                                 /* someone else has already taken it - set as not active */
167                                                 agrp->flag &= ~AGRP_ACTIVE;
168                                         }
169                                 }
170                                 else {
171                                         /* this can't possibly be active now */
172                                         agrp->flag &= ~AGRP_ACTIVE;
173                                 }
174
175                                 /* sync group colors */
176                                 bgrp = (bActionGroup *)BLI_findlink(&ob->pose->agroups, (pchan->agrp_index - 1));
177                                 if (bgrp) {
178                                         agrp->customCol = bgrp->customCol;
179                                         action_group_colors_sync(agrp, bgrp);
180                                 }
181                         }
182                 }
183         }
184 }
185
186 /* perform syncing updates for F-Curves */
187 static void animchan_sync_fcurve(bAnimContext *ac, bAnimListElem *ale, FCurve **active_fcurve)
188 {
189         FCurve *fcu = (FCurve *)ale->data;
190         ID *owner_id = ale->id;
191
192         /* major priority is selection status, so refer to the checks done in anim_filter.c
193          * skip_fcurve_selected_data() for reference about what's going on here...
194          */
195         if (ELEM(NULL, fcu, fcu->rna_path, owner_id))
196                 return;
197
198         if (GS(owner_id->name) == ID_SCE) {
199                 Scene *scene = (Scene *)owner_id;
200
201                 /* only affect if F-Curve involves sequence_editor.sequences */
202                 if ((fcu->rna_path) && strstr(fcu->rna_path, "sequences_all")) {
203                         Editing *ed = BKE_sequencer_editing_get(scene, false);
204                         Sequence *seq;
205                         char *seq_name;
206
207                         /* get strip name, and check if this strip is selected */
208                         seq_name = BLI_str_quoted_substrN(fcu->rna_path, "sequences_all[");
209                         seq = BKE_sequence_get_by_name(ed->seqbasep, seq_name, false);
210                         if (seq_name) MEM_freeN(seq_name);
211
212                         /* update selection status */
213                         if (seq) {
214                                 if (seq->flag & SELECT)
215                                         fcu->flag |= FCURVE_SELECTED;
216                                 else
217                                         fcu->flag &= ~FCURVE_SELECTED;
218                         }
219                 }
220         }
221         else if (GS(owner_id->name) == ID_NT) {
222                 bNodeTree *ntree = (bNodeTree *)owner_id;
223
224                 /* check for selected nodes */
225                 if ((fcu->rna_path) && strstr(fcu->rna_path, "nodes")) {
226                         bNode *node;
227                         char *node_name;
228
229                         /* get strip name, and check if this strip is selected */
230                         node_name = BLI_str_quoted_substrN(fcu->rna_path, "nodes[");
231                         node = nodeFindNodebyName(ntree, node_name);
232                         if (node_name) MEM_freeN(node_name);
233
234                         /* update selection/active status */
235                         if (node) {
236                                 /* update selection status */
237                                 if (node->flag & NODE_SELECT)
238                                         fcu->flag |= FCURVE_SELECTED;
239                                 else
240                                         fcu->flag &= ~FCURVE_SELECTED;
241
242                                 /* update active status */
243                                 /* XXX: this may interfere with setting bones as active if both exist at once;
244                                  * then again, if that's the case, production setups aren't likely to be animating
245                                  * nodes while working with bones?
246                                  */
247                                 if (node->flag & NODE_ACTIVE) {
248                                         if (*active_fcurve == NULL) {
249                                                 fcu->flag |= FCURVE_ACTIVE;
250                                                 *active_fcurve = fcu;
251                                         }
252                                         else {
253                                                 fcu->flag &= ~FCURVE_ACTIVE;
254                                         }
255                                 }
256                                 else {
257                                         fcu->flag &= ~FCURVE_ACTIVE;
258                                 }
259                         }
260                 }
261         }
262 }
263
264 /* perform syncing updates for GPencil Layers */
265 static void animchan_sync_gplayer(bAnimContext *UNUSED(ac), bAnimListElem *ale)
266 {
267         bGPDlayer *gpl = (bGPDlayer *)ale->data;
268
269         /* Make sure the selection flags agree with the "active" flag.
270          * The selection flags are used in the Dopesheet only, whereas
271          * the active flag is used everywhere else. Hence, we try to
272          * sync these here so that it all seems to be have as the user
273          * expects - T50184
274          *
275          * Assume that we only really do this when the active status changes.
276          * (NOTE: This may prove annoying if it means selection is always lost)
277          */
278         if (gpl->flag & GP_LAYER_ACTIVE) {
279                 gpl->flag |= GP_LAYER_SELECT;
280         }
281         else {
282                 gpl->flag &= ~GP_LAYER_SELECT;
283         }
284 }
285
286 /* ---------------- */
287
288 /* Main call to be exported to animation editors */
289 void ANIM_sync_animchannels_to_data(const bContext *C)
290 {
291         bAnimContext ac;
292         ListBase anim_data = {NULL, NULL};
293         bAnimListElem *ale;
294         int filter;
295
296         bActionGroup *active_agrp = NULL;
297         FCurve *active_fcurve = NULL;
298
299         /* get animation context info for filtering the channels */
300         if (ANIM_animdata_get_context(C, &ac) == 0)
301                 return;
302
303         /* filter data */
304         /* NOTE: we want all channels, since we want to be able to set selection status on some of them even when collapsed
305          *       However, don't include duplicates so that selection statuses don't override each other
306          */
307         filter = ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_CHANNELS | ANIMFILTER_NODUPLIS;
308         ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
309
310         /* flush settings as appropriate depending on the types of the channels */
311         for (ale = anim_data.first; ale; ale = ale->next) {
312                 switch (ale->type) {
313                         case ANIMTYPE_GROUP:
314                                 animchan_sync_group(&ac, ale, &active_agrp);
315                                 break;
316
317                         case ANIMTYPE_FCURVE:
318                                 animchan_sync_fcurve(&ac, ale, &active_fcurve);
319                                 break;
320
321                         case ANIMTYPE_GPLAYER:
322                                 animchan_sync_gplayer(&ac, ale);
323                                 break;
324                 }
325         }
326
327         ANIM_animdata_freelist(&anim_data);
328 }
329
330 void ANIM_animdata_update(bAnimContext *ac, ListBase *anim_data)
331 {
332         bAnimListElem *ale;
333
334         if (ELEM(ac->datatype, ANIMCONT_MASK)) {
335 #ifdef DEBUG
336                 /* quiet assert */
337                 for (ale = anim_data->first; ale; ale = ale->next) {
338                         ale->update = 0;
339                 }
340 #endif
341                 return;
342         }
343
344         for (ale = anim_data->first; ale; ale = ale->next) {
345                 if (ale->type == ANIMTYPE_GPLAYER) {
346                         bGPDlayer *gpl = ale->data;
347
348                         if (ale->update & ANIM_UPDATE_ORDER) {
349                                 ale->update &= ~ANIM_UPDATE_ORDER;
350                                 if (gpl) {
351                                         //gpencil_sort_frames(gpl);
352                                 }
353                         }
354
355                         if (ale->update & ANIM_UPDATE_DEPS) {
356                                 ale->update &= ~ANIM_UPDATE_DEPS;
357                                 ANIM_list_elem_update(ac->bmain, ac->scene, ale);
358                         }
359                         /* disable handles to avoid crash */
360                         if (ale->update & ANIM_UPDATE_HANDLES) {
361                                 ale->update &= ~ANIM_UPDATE_HANDLES;
362                         }
363                 }
364                 else if (ale->datatype == ALE_FCURVE) {
365                         FCurve *fcu = ale->key_data;
366
367                         if (ale->update & ANIM_UPDATE_ORDER) {
368                                 ale->update &= ~ANIM_UPDATE_ORDER;
369                                 if (fcu)
370                                         sort_time_fcurve(fcu);
371                         }
372
373                         if (ale->update & ANIM_UPDATE_HANDLES) {
374                                 ale->update &= ~ANIM_UPDATE_HANDLES;
375                                 if (fcu)
376                                         calchandles_fcurve(fcu);
377                         }
378
379                         if (ale->update & ANIM_UPDATE_DEPS) {
380                                 ale->update &= ~ANIM_UPDATE_DEPS;
381                                 ANIM_list_elem_update(ac->bmain, ac->scene, ale);
382                         }
383                 }
384                 else if (ELEM(ale->type, ANIMTYPE_ANIMDATA, ANIMTYPE_NLAACTION, ANIMTYPE_NLATRACK, ANIMTYPE_NLACURVE)) {
385                         if (ale->update & ANIM_UPDATE_DEPS) {
386                                 ale->update &= ~ANIM_UPDATE_DEPS;
387                                 ANIM_list_elem_update(ac->bmain, ac->scene, ale);
388                         }
389                 }
390                 else if (ale->update) {
391 #if 0
392                         if (G.debug & G_DEBUG) {
393                                 printf("%s: Unhandled animchannel updates (%d) for type=%d (%p)\n",
394                                        __func__, ale->update, ale->type, ale->data);
395                         }
396 #endif
397                         /* Prevent crashes in cases where it can't be handled */
398                         ale->update = 0;
399                 }
400
401                 BLI_assert(ale->update == 0);
402         }
403 }
404
405 void ANIM_animdata_freelist(ListBase *anim_data)
406 {
407 #ifndef NDEBUG
408         bAnimListElem *ale, *ale_next;
409         for (ale = anim_data->first; ale; ale = ale_next) {
410                 ale_next = ale->next;
411                 BLI_assert(ale->update == 0);
412                 MEM_freeN(ale);
413         }
414         BLI_listbase_clear(anim_data);
415 #else
416         BLI_freelistN(anim_data);
417 #endif
418 }