Merge branch 'master' into blender2.8
[blender.git] / source / blender / editors / animation / anim_channels_edit.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  * Contributor(s): Joshua Leung
22  *
23  * ***** END GPL LICENSE BLOCK *****
24  */
25
26 /** \file blender/editors/animation/anim_channels_edit.c
27  *  \ingroup edanimation
28  */
29
30
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h> 
34
35 #include "MEM_guardedalloc.h"
36
37 #include "BKE_depsgraph.h"
38 #include "BLI_blenlib.h"
39 #include "BLI_utildefines.h"
40 #include "BLI_listbase.h"
41 #include "BKE_library.h"
42
43 #include "DNA_anim_types.h"
44 #include "DNA_object_types.h"
45 #include "DNA_scene_types.h"
46 #include "DNA_key_types.h"
47 #include "DNA_gpencil_types.h"
48 #include "DNA_mask_types.h"
49
50 #include "RNA_access.h"
51 #include "RNA_define.h"
52
53 #include "BKE_animsys.h"
54 #include "BKE_action.h"
55 #include "BKE_fcurve.h"
56 #include "BKE_gpencil.h"
57 #include "BKE_context.h"
58 #include "BKE_mask.h"
59 #include "BKE_global.h"
60
61 #include "UI_view2d.h"
62
63 #include "ED_anim_api.h"
64 #include "ED_armature.h"
65 #include "ED_keyframes_edit.h" // XXX move the select modes out of there!
66 #include "ED_object.h"
67 #include "ED_screen.h"
68
69 #include "WM_api.h"
70 #include "WM_types.h"
71
72 /* ************************************************************************** */
73 /* CHANNELS API - Exposed API */
74
75 /* -------------------------- Selection ------------------------------------- */
76
77 /* Set the given animation-channel as the active one for the active context */
78 // TODO: extend for animdata types...
79 void ANIM_set_active_channel(bAnimContext *ac, void *data, eAnimCont_Types datatype, eAnimFilter_Flags filter, void *channel_data, eAnim_ChannelType channel_type)
80 {
81         ListBase anim_data = {NULL, NULL};
82         bAnimListElem *ale;
83         
84         /* try to build list of filtered items */
85         ANIM_animdata_filter(ac, &anim_data, filter, data, datatype);
86         if (BLI_listbase_is_empty(&anim_data))
87                 return;
88                 
89         /* only clear the 'active' flag for the channels of the same type */
90         for (ale = anim_data.first; ale; ale = ale->next) {
91                 /* skip if types don't match */
92                 if (channel_type != ale->type)
93                         continue;
94                 
95                 /* flag to set depends on type */
96                 switch (ale->type) {
97                         case ANIMTYPE_GROUP:
98                         {
99                                 bActionGroup *agrp = (bActionGroup *)ale->data;
100                                 
101                                 ACHANNEL_SET_FLAG(agrp, ACHANNEL_SETFLAG_CLEAR, AGRP_ACTIVE);
102                                 break;
103                         }
104                         case ANIMTYPE_FCURVE:
105                         case ANIMTYPE_NLACURVE:
106                         {
107                                 FCurve *fcu = (FCurve *)ale->data;
108                                 
109                                 ACHANNEL_SET_FLAG(fcu, ACHANNEL_SETFLAG_CLEAR, FCURVE_ACTIVE);
110                                 break;
111                         }
112                         case ANIMTYPE_NLATRACK:
113                         {
114                                 NlaTrack *nlt = (NlaTrack *)ale->data;
115                                 
116                                 ACHANNEL_SET_FLAG(nlt, ACHANNEL_SETFLAG_CLEAR, NLATRACK_ACTIVE);
117                                 break;
118                         }
119                         case ANIMTYPE_FILLACTD: /* Action Expander */
120                         case ANIMTYPE_DSMAT:    /* Datablock AnimData Expanders */
121                         case ANIMTYPE_DSLAM:
122                         case ANIMTYPE_DSCAM:
123                         case ANIMTYPE_DSCUR:
124                         case ANIMTYPE_DSSKEY:
125                         case ANIMTYPE_DSWOR:
126                         case ANIMTYPE_DSMBALL:
127                         case ANIMTYPE_DSARM:
128                         case ANIMTYPE_DSMESH:
129                         case ANIMTYPE_DSTEX:
130                         case ANIMTYPE_DSLAT:
131                         case ANIMTYPE_DSLINESTYLE:
132                         case ANIMTYPE_DSSPK:
133                         case ANIMTYPE_DSGPENCIL:
134                         {
135                                 /* need to verify that this data is valid for now */
136                                 if (ale->adt) {
137                                         ACHANNEL_SET_FLAG(ale->adt, ACHANNEL_SETFLAG_CLEAR, ADT_UI_ACTIVE);
138                                 }
139                                 break;
140                         }
141                         case ANIMTYPE_GPLAYER:
142                         {
143                                 bGPDlayer *gpl = (bGPDlayer *)ale->data;
144                                 
145                                 ACHANNEL_SET_FLAG(gpl, ACHANNEL_SETFLAG_CLEAR, GP_LAYER_ACTIVE);
146                                 break;
147                         }
148                 }
149         }
150         
151         /* set active flag */
152         if (channel_data) {
153                 switch (channel_type) {
154                         case ANIMTYPE_GROUP:
155                         {
156                                 bActionGroup *agrp = (bActionGroup *)channel_data;
157                                 agrp->flag |= AGRP_ACTIVE;
158                                 break;
159                         }
160                         case ANIMTYPE_FCURVE:
161                         case ANIMTYPE_NLACURVE:
162                         {
163                                 FCurve *fcu = (FCurve *)channel_data;
164                                 fcu->flag |= FCURVE_ACTIVE;
165                                 break;
166                         }
167                         case ANIMTYPE_NLATRACK:
168                         {
169                                 NlaTrack *nlt = (NlaTrack *)channel_data;
170                                 nlt->flag |= NLATRACK_ACTIVE;
171                                 break;
172                         }
173                         case ANIMTYPE_FILLACTD: /* Action Expander */
174                         case ANIMTYPE_DSMAT:    /* Datablock AnimData Expanders */
175                         case ANIMTYPE_DSLAM:
176                         case ANIMTYPE_DSCAM:
177                         case ANIMTYPE_DSCUR:
178                         case ANIMTYPE_DSSKEY:
179                         case ANIMTYPE_DSWOR:
180                         case ANIMTYPE_DSMBALL:
181                         case ANIMTYPE_DSARM:
182                         case ANIMTYPE_DSMESH:
183                         case ANIMTYPE_DSLAT:
184                         case ANIMTYPE_DSLINESTYLE:
185                         case ANIMTYPE_DSSPK:
186                         case ANIMTYPE_DSNTREE:
187                         case ANIMTYPE_DSTEX:
188                         case ANIMTYPE_DSGPENCIL:
189                         {
190                                 /* need to verify that this data is valid for now */
191                                 if (ale && ale->adt) {
192                                         ale->adt->flag |= ADT_UI_ACTIVE;
193                                 }
194                                 break;
195                         }
196                         
197                         case ANIMTYPE_GPLAYER:
198                         {
199                                 bGPDlayer *gpl = (bGPDlayer *)channel_data;
200                                 gpl->flag |= GP_LAYER_ACTIVE;
201                                 break;
202                         }
203                         
204                         /* unhandled currently, but may be interesting */
205                         case ANIMTYPE_MASKLAYER:
206                         case ANIMTYPE_SHAPEKEY:
207                         case ANIMTYPE_NLAACTION:
208                                 break;
209                         
210                         /* other types */
211                         default:
212                                 break;
213                 }
214         }
215         
216         /* clean up */
217         ANIM_animdata_freelist(&anim_data);
218 }
219
220 /* Deselect all animation channels 
221  *      - data: pointer to datatype, as contained in bAnimContext
222  *      - datatype: the type of data that 'data' represents (eAnimCont_Types)
223  *      - test: check if deselecting instead of selecting
224  *      - sel: eAnimChannels_SetFlag;
225  */
226 void ANIM_deselect_anim_channels(bAnimContext *ac, void *data, eAnimCont_Types datatype, bool test, eAnimChannels_SetFlag sel)
227 {
228         ListBase anim_data = {NULL, NULL};
229         bAnimListElem *ale;
230         int filter;
231         
232         /* filter data */
233         /* NOTE: no list visible, otherwise, we get dangling */
234         filter = ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_CHANNELS;
235         ANIM_animdata_filter(ac, &anim_data, filter, data, datatype);
236         
237         /* See if we should be selecting or deselecting */
238         if (test) {
239                 for (ale = anim_data.first; ale; ale = ale->next) {
240                         if (sel == 0) 
241                                 break;
242                         
243                         switch (ale->type) {
244                                 case ANIMTYPE_SCENE:
245                                         if (ale->flag & SCE_DS_SELECTED)
246                                                 sel = ACHANNEL_SETFLAG_CLEAR;
247                                         break;
248                                 case ANIMTYPE_OBJECT:
249 #if 0   /* for now, do not take object selection into account, since it gets too annoying */
250                                         if (ale->flag & SELECT)
251                                                 sel = ACHANNEL_SETFLAG_CLEAR;
252 #endif
253                                         break;
254                                 case ANIMTYPE_GROUP:
255                                         if (ale->flag & AGRP_SELECTED)
256                                                 sel = ACHANNEL_SETFLAG_CLEAR;
257                                         break;
258                                 case ANIMTYPE_FCURVE:
259                                 case ANIMTYPE_NLACURVE:
260                                         if (ale->flag & FCURVE_SELECTED)
261                                                 sel = ACHANNEL_SETFLAG_CLEAR;
262                                         break;
263                                 case ANIMTYPE_SHAPEKEY:
264                                         if (ale->flag & KEYBLOCK_SEL)
265                                                 sel = ACHANNEL_SETFLAG_CLEAR;
266                                         break;
267                                 case ANIMTYPE_NLATRACK:
268                                         if (ale->flag & NLATRACK_SELECTED)
269                                                 sel = ACHANNEL_SETFLAG_CLEAR;
270                                         break;
271                                         
272                                 case ANIMTYPE_FILLACTD: /* Action Expander */
273                                 case ANIMTYPE_DSMAT:    /* Datablock AnimData Expanders */
274                                 case ANIMTYPE_DSLAM:
275                                 case ANIMTYPE_DSCAM:
276                                 case ANIMTYPE_DSCUR:
277                                 case ANIMTYPE_DSSKEY:
278                                 case ANIMTYPE_DSWOR:
279                                 case ANIMTYPE_DSMBALL:
280                                 case ANIMTYPE_DSARM:
281                                 case ANIMTYPE_DSMESH:
282                                 case ANIMTYPE_DSNTREE:
283                                 case ANIMTYPE_DSTEX:
284                                 case ANIMTYPE_DSLAT:
285                                 case ANIMTYPE_DSLINESTYLE:
286                                 case ANIMTYPE_DSSPK:
287                                 case ANIMTYPE_DSGPENCIL:
288                                 {
289                                         if ((ale->adt) && (ale->adt->flag & ADT_UI_SELECTED))
290                                                 sel = ACHANNEL_SETFLAG_CLEAR;
291                                         break;
292                                 }
293                                 case ANIMTYPE_GPLAYER:
294                                         if (ale->flag & GP_LAYER_SELECT)
295                                                 sel = ACHANNEL_SETFLAG_CLEAR;
296                                         break;
297                                 case ANIMTYPE_MASKLAYER:
298                                         if (ale->flag & MASK_LAYERFLAG_SELECT)
299                                                 sel = ACHANNEL_SETFLAG_CLEAR;
300                                         break;
301                         }
302                 }
303         }
304                 
305         /* Now set the flags */
306         for (ale = anim_data.first; ale; ale = ale->next) {
307                 switch (ale->type) {
308                         case ANIMTYPE_SCENE:
309                         {
310                                 Scene *scene = (Scene *)ale->data;
311                                 
312                                 ACHANNEL_SET_FLAG(scene, sel, SCE_DS_SELECTED);
313                                 
314                                 if (scene->adt) {
315                                         ACHANNEL_SET_FLAG(scene, sel, ADT_UI_SELECTED);
316                                 }
317                                 break;
318                         }
319                         case ANIMTYPE_OBJECT:
320                         {
321 #if 0   /* for now, do not take object selection into account, since it gets too annoying */
322                                 Base *base = (Base *)ale->data;
323                                 Object *ob = base->object;
324
325                                 ACHANNEL_SET_FLAG(base, sel, SELECT);
326                                 ACHANNEL_SET_FLAG(ob, sel, SELECT);
327
328                                 if (ob->adt) {
329                                         ACHANNEL_SET_FLAG(ob, sel, ADT_UI_SELECTED);
330                                 }
331 #endif
332                                 break;
333                         }
334                         case ANIMTYPE_GROUP:
335                         {
336                                 bActionGroup *agrp = (bActionGroup *)ale->data;
337                                 
338                                 ACHANNEL_SET_FLAG(agrp, sel, AGRP_SELECTED);
339                                 agrp->flag &= ~AGRP_ACTIVE;
340                                 break;
341                         }
342                         case ANIMTYPE_FCURVE:
343                         case ANIMTYPE_NLACURVE:
344                         {
345                                 FCurve *fcu = (FCurve *)ale->data;
346                                 
347                                 ACHANNEL_SET_FLAG(fcu, sel, FCURVE_SELECTED);
348                                 fcu->flag &= ~FCURVE_ACTIVE;
349                                 break;
350                         }
351                         case ANIMTYPE_SHAPEKEY:
352                         {
353                                 KeyBlock *kb = (KeyBlock *)ale->data;
354                                 
355                                 ACHANNEL_SET_FLAG(kb, sel, KEYBLOCK_SEL);
356                                 break;
357                         }
358                         case ANIMTYPE_NLATRACK:
359                         {
360                                 NlaTrack *nlt = (NlaTrack *)ale->data;
361                                 
362                                 ACHANNEL_SET_FLAG(nlt, sel, NLATRACK_SELECTED);
363                                 nlt->flag &= ~NLATRACK_ACTIVE;
364                                 break;
365                         }
366                         case ANIMTYPE_FILLACTD: /* Action Expander */
367                         case ANIMTYPE_DSMAT:    /* Datablock AnimData Expanders */
368                         case ANIMTYPE_DSLAM:
369                         case ANIMTYPE_DSCAM:
370                         case ANIMTYPE_DSCUR:
371                         case ANIMTYPE_DSSKEY:
372                         case ANIMTYPE_DSWOR:
373                         case ANIMTYPE_DSMBALL:
374                         case ANIMTYPE_DSARM:
375                         case ANIMTYPE_DSMESH:
376                         case ANIMTYPE_DSNTREE:
377                         case ANIMTYPE_DSTEX:
378                         case ANIMTYPE_DSLAT:
379                         case ANIMTYPE_DSLINESTYLE:
380                         case ANIMTYPE_DSSPK:
381                         case ANIMTYPE_DSGPENCIL:
382                         {
383                                 /* need to verify that this data is valid for now */
384                                 if (ale->adt) {
385                                         ACHANNEL_SET_FLAG(ale->adt, sel, ADT_UI_SELECTED);
386                                         ale->adt->flag &= ~ADT_UI_ACTIVE;
387                                 }
388                                 break;
389                         }
390                         case ANIMTYPE_GPLAYER:
391                         {
392                                 bGPDlayer *gpl = (bGPDlayer *)ale->data;
393                                 
394                                 ACHANNEL_SET_FLAG(gpl, sel, GP_LAYER_SELECT);
395                                 break;
396                         }
397                         case ANIMTYPE_MASKLAYER:
398                         {
399                                 MaskLayer *masklay = (MaskLayer *)ale->data;
400                                 
401                                 ACHANNEL_SET_FLAG(masklay, sel, MASK_LAYERFLAG_SELECT);
402                                 break;
403                         }
404                 }
405         }
406         
407         /* Cleanup */
408         ANIM_animdata_freelist(&anim_data);
409 }
410
411 /* ---------------------------- Graph Editor ------------------------------------- */
412
413 /* Flush visibility (for Graph Editor) changes up/down hierarchy for changes in the given setting 
414  *      - anim_data: list of the all the anim channels that can be chosen
415  *              -> filtered using ANIMFILTER_CHANNELS only, since if we took VISIBLE too,
416  *                then the channels under closed expanders get ignored...
417  *      - ale_setting: the anim channel (not in the anim_data list directly, though occurring there)
418  *              with the new state of the setting that we want flushed up/down the hierarchy 
419  *      - setting: type of setting to set
420  *      - on: whether the visibility setting has been enabled or disabled 
421  */
422 void ANIM_flush_setting_anim_channels(bAnimContext *ac, ListBase *anim_data, bAnimListElem *ale_setting, eAnimChannel_Settings setting, eAnimChannels_SetFlag mode)
423 {
424         bAnimListElem *ale, *match = NULL;
425         int prevLevel = 0, matchLevel = 0;
426         
427         /* sanity check */
428         if (ELEM(NULL, anim_data, anim_data->first))
429                 return;
430         
431         /* find the channel that got changed */
432         for (ale = anim_data->first; ale; ale = ale->next) {
433                 /* compare data, and type as main way of identifying the channel */
434                 if ((ale->data == ale_setting->data) && (ale->type == ale_setting->type)) {
435                         /* we also have to check the ID, this is assigned to, since a block may have multiple users */
436                         /* TODO: is the owner-data more revealing? */
437                         if (ale->id == ale_setting->id) {
438                                 match = ale;
439                                 break;
440                         }
441                 }
442         }
443         if (match == NULL) {
444                 printf("ERROR: no channel matching the one changed was found\n");
445                 return;
446         }
447         else {
448                 const bAnimChannelType *acf = ANIM_channel_get_typeinfo(ale_setting);
449                 
450                 if (acf == NULL) {
451                         printf("ERROR: no channel info for the changed channel\n");
452                         return;
453                 }
454                 
455                 /* get the level of the channel that was affected
456                  *   - we define the level as simply being the offset for the start of the channel
457                  */
458                 matchLevel = (acf->get_offset) ? acf->get_offset(ac, ale_setting) : 0;
459                 prevLevel = matchLevel;
460         }
461         
462         /* flush up? 
463          *
464          * For Visibility:
465          *      - only flush up if the current state is now enabled (positive 'on' state is default) 
466          *        (otherwise, it's too much work to force the parents to be inactive too)
467          *
468          * For everything else:
469          *      - only flush up if the current state is now disabled (negative 'off' state is default)
470          *        (otherwise, it's too much work to force the parents to be active too)
471          */
472         if ( ((setting == ACHANNEL_SETTING_VISIBLE) && (mode != ACHANNEL_SETFLAG_CLEAR)) ||
473              ((setting != ACHANNEL_SETTING_VISIBLE) && (mode == ACHANNEL_SETFLAG_CLEAR)))
474         {
475                 /* go backwards in the list, until the highest-ranking element (by indention has been covered) */
476                 for (ale = match->prev; ale; ale = ale->prev) {
477                         const bAnimChannelType *acf = ANIM_channel_get_typeinfo(ale);
478                         int level;
479                         
480                         /* if no channel info was found, skip, since this type might not have any useful info */
481                         if (acf == NULL)
482                                 continue;
483                         
484                         /* get the level of the current channel traversed 
485                          *   - we define the level as simply being the offset for the start of the channel
486                          */
487                         level = (acf->get_offset) ? acf->get_offset(ac, ale) : 0;
488                         
489                         /* if the level is 'less than' (i.e. more important) the level we're matching
490                          * but also 'less than' the level just tried (i.e. only the 1st group above grouped F-Curves, 
491                          * when toggling visibility of F-Curves, gets flushed, which should happen if we don't let prevLevel
492                          * get updated below once the first 1st group is found)...
493                          */
494                         if (level < prevLevel) {
495                                 /* flush the new status... */
496                                 ANIM_channel_setting_set(ac, ale, setting, mode);
497                                 
498                                 /* store this level as the 'old' level now */
499                                 prevLevel = level;
500                         }
501                         /* if the level is 'greater than' (i.e. less important) than the previous level... */
502                         else if (level > prevLevel) {
503                                 /* if previous level was a base-level (i.e. 0 offset / root of one hierarchy),
504                                  * stop here
505                                  */
506                                 if (prevLevel == 0)
507                                         break;
508                                 /* otherwise, this level weaves into another sibling hierarchy to the previous one just
509                                  * finished, so skip until we get to the parent of this level 
510                                  */
511                                 else
512                                         continue;
513                         }
514                 }
515         }
516         
517         /* flush down (always) */
518         {
519                 /* go forwards in the list, until the lowest-ranking element (by indention has been covered) */
520                 for (ale = match->next; ale; ale = ale->next) {
521                         const bAnimChannelType *acf = ANIM_channel_get_typeinfo(ale);
522                         int level;
523                         
524                         /* if no channel info was found, skip, since this type might not have any useful info */
525                         if (acf == NULL)
526                                 continue;
527                         
528                         /* get the level of the current channel traversed 
529                          *   - we define the level as simply being the offset for the start of the channel
530                          */
531                         level = (acf->get_offset) ? acf->get_offset(ac, ale) : 0;
532                         
533                         /* if the level is 'greater than' (i.e. less important) the channel that was changed, 
534                          * flush the new status...
535                          */
536                         if (level > matchLevel)
537                                 ANIM_channel_setting_set(ac, ale, setting, mode);
538                         /* however, if the level is 'less than or equal to' the channel that was changed,
539                          * (i.e. the current channel is as important if not more important than the changed channel)
540                          * then we should stop, since we've found the last one of the children we should flush
541                          */
542                         else
543                                 break;
544                         
545                         /* store this level as the 'old' level now */
546                         // prevLevel = level; // XXX: prevLevel is unused
547                 }
548         }
549 }
550
551 /* -------------------------- F-Curves ------------------------------------- */
552
553 /* Delete the given F-Curve from its AnimData block */
554 void ANIM_fcurve_delete_from_animdata(bAnimContext *ac, AnimData *adt, FCurve *fcu)
555 {
556         /* - if no AnimData, we've got nowhere to remove the F-Curve from 
557          *      (this doesn't guarantee that the F-Curve is in there, but at least we tried
558          * - if no F-Curve, there is nothing to remove
559          */
560         if (ELEM(NULL, adt, fcu))
561                 return;
562                 
563         /* remove from whatever list it came from
564          *      - Action Group
565          *      - Action
566          *      - Drivers
567          *      - TODO... some others?
568          */
569         if ((ac) && (ac->datatype == ANIMCONT_DRIVERS)) {
570                 /* driver F-Curve */
571                 BLI_remlink(&adt->drivers, fcu);
572         }
573         else if (adt->action) {
574                 bAction *act = adt->action;
575                 
576                 /* remove from group or action, whichever one "owns" the F-Curve */
577                 if (fcu->grp) {
578                         bActionGroup *agrp = fcu->grp;
579                         
580                         /* remove F-Curve from group+action */
581                         action_groups_remove_channel(act, fcu);
582                         
583                         /* if group has no more channels, remove it too, 
584                          * otherwise can have many dangling groups [#33541]
585                          */
586                         if (BLI_listbase_is_empty(&agrp->channels)) {
587                                 BLI_freelinkN(&act->groups, agrp);
588                         }
589                 }
590                 else {
591                         BLI_remlink(&act->curves, fcu);
592                 }
593                 
594                 /* if action has no more F-Curves as a result of this, unlink it from
595                  * AnimData if it did not come from a NLA Strip being tweaked.
596                  *
597                  * This is done so that we don't have dangling Object+Action entries in
598                  * channel list that are empty, and linger around long after the data they
599                  * are for has disappeared (and probably won't come back).
600                  */
601                 if (BLI_listbase_is_empty(&act->curves) && (adt->flag & ADT_NLA_EDIT_ON) == 0) {
602                         id_us_min(&act->id);
603                         adt->action = NULL;
604                 }
605         }
606                 
607         /* free the F-Curve itself */
608         free_fcurve(fcu);
609 }
610
611 /* ************************************************************************** */
612 /* OPERATORS */
613
614 /* ****************** Operator Utilities ********************************** */
615
616 /* poll callback for being in an Animation Editor channels list region */
617 static int animedit_poll_channels_active(bContext *C)
618 {
619         ScrArea *sa = CTX_wm_area(C);
620         
621         /* channels region test */
622         /* TODO: could enhance with actually testing if channels region? */
623         if (ELEM(NULL, sa, CTX_wm_region(C)))
624                 return 0;
625         /* animation editor test */
626         if (ELEM(sa->spacetype, SPACE_ACTION, SPACE_IPO, SPACE_NLA) == 0)
627                 return 0;
628
629         return 1;
630 }
631
632 /* poll callback for Animation Editor channels list region + not in NLA-tweakmode for NLA */
633 static int animedit_poll_channels_nla_tweakmode_off(bContext *C)
634 {
635         ScrArea *sa = CTX_wm_area(C);
636         Scene *scene = CTX_data_scene(C);
637
638         /* channels region test */
639         /* TODO: could enhance with actually testing if channels region? */
640         if (ELEM(NULL, sa, CTX_wm_region(C)))
641                 return 0;
642         /* animation editor test */
643         if (ELEM(sa->spacetype, SPACE_ACTION, SPACE_IPO, SPACE_NLA) == 0)
644                 return 0;
645         
646         /* NLA TweakMode test */
647         if (sa->spacetype == SPACE_NLA) {
648                 if ((scene == NULL) || (scene->flag & SCE_NLA_EDIT_ON))
649                         return 0;
650         }
651                 
652         return 1;
653 }
654
655 /* ****************** Rearrange Channels Operator ******************* */
656
657 /* constants for channel rearranging */
658 /* WARNING: don't change existing ones without modifying rearrange func accordingly */
659 typedef enum eRearrangeAnimChan_Mode {
660         REARRANGE_ANIMCHAN_TOP = -2,
661         REARRANGE_ANIMCHAN_UP = -1,
662         REARRANGE_ANIMCHAN_DOWN = 1,
663         REARRANGE_ANIMCHAN_BOTTOM = 2
664 } eRearrangeAnimChan_Mode;
665
666 /* defines for rearranging channels */
667 static EnumPropertyItem prop_animchannel_rearrange_types[] = {
668         {REARRANGE_ANIMCHAN_TOP, "TOP", 0, "To Top", ""},
669         {REARRANGE_ANIMCHAN_UP, "UP", 0, "Up", ""},
670         {REARRANGE_ANIMCHAN_DOWN, "DOWN", 0, "Down", ""},
671         {REARRANGE_ANIMCHAN_BOTTOM, "BOTTOM", 0, "To Bottom", ""},
672         {0, NULL, 0, NULL, NULL}
673 };
674
675 /* Reordering "Islands" Defines ----------------------------------- */
676
677 /* Island definition - just a listbase container */
678 typedef struct tReorderChannelIsland {
679         struct tReorderChannelIsland *next, *prev;
680         
681         ListBase channels;  /* channels within this region with the same state */
682         int flag;           /* eReorderIslandFlag */
683 } tReorderChannelIsland;
684
685 /* flags for channel reordering islands */
686 typedef enum eReorderIslandFlag {
687         REORDER_ISLAND_SELECTED         = (1 << 0),   /* island is selected */
688         REORDER_ISLAND_UNTOUCHABLE      = (1 << 1),   /* island should be ignored */
689         REORDER_ISLAND_MOVED            = (1 << 2),   /* island has already been moved */
690         REORDER_ISLAND_HIDDEN           = (1 << 3),   /* island is not visible */
691 } eReorderIslandFlag;
692
693
694 /* Rearrange Methods --------------------------------------------- */
695
696 static bool rearrange_island_ok(tReorderChannelIsland *island)
697 {
698         /* island must not be untouchable */
699         if (island->flag & REORDER_ISLAND_UNTOUCHABLE)
700                 return 0;
701         
702         /* island should be selected to be moved */
703         return (island->flag & REORDER_ISLAND_SELECTED) && !(island->flag & REORDER_ISLAND_MOVED);
704 }
705
706 /* ............................. */
707
708 static bool rearrange_island_top(ListBase *list, tReorderChannelIsland *island)
709 {
710         if (rearrange_island_ok(island)) {
711                 /* remove from current position */
712                 BLI_remlink(list, island);
713                 
714                 /* make it first element */
715                 BLI_insertlinkbefore(list, list->first, island);
716                 
717                 return 1;
718         }
719         
720         return 0;
721 }
722
723 static bool rearrange_island_up(ListBase *list, tReorderChannelIsland *island)
724 {
725         if (rearrange_island_ok(island)) {
726                 /* moving up = moving before the previous island, otherwise we're in the same place */
727                 tReorderChannelIsland *prev = island->prev;
728
729                 /* Skip hidden islands! */
730                 while (prev && prev->flag & REORDER_ISLAND_HIDDEN) {
731                         prev = prev->prev;
732                 }
733
734                 if (prev) {
735                         /* remove from current position */
736                         BLI_remlink(list, island);
737                         
738                         /* push it up */
739                         BLI_insertlinkbefore(list, prev, island);
740                         
741                         return 1;
742                 }
743         }
744         
745         return 0;
746 }
747
748 static bool rearrange_island_down(ListBase *list, tReorderChannelIsland *island)
749 {
750         if (rearrange_island_ok(island)) {
751                 /* moving down = moving after the next island, otherwise we're in the same place */
752                 tReorderChannelIsland *next = island->next;
753
754                 /* Skip hidden islands! */
755                 while (next && next->flag & REORDER_ISLAND_HIDDEN) {
756                         next = next->next;
757                 }
758
759                 if (next) {
760                         /* can only move past if next is not untouchable (i.e. nothing can go after it) */
761                         if ((next->flag & REORDER_ISLAND_UNTOUCHABLE) == 0) {
762                                 /* remove from current position */
763                                 BLI_remlink(list, island);
764                                 
765                                 /* push it down */
766                                 BLI_insertlinkafter(list, next, island);
767                                 
768                                 return true;
769                         }
770                 }
771                 /* else: no next channel, so we're at the bottom already, so can't move */
772         }
773         
774         return false;
775 }
776
777 static bool rearrange_island_bottom(ListBase *list, tReorderChannelIsland *island)
778 {
779         if (rearrange_island_ok(island)) {
780                 tReorderChannelIsland *last = list->last;
781                 
782                 /* remove island from current position */
783                 BLI_remlink(list, island);
784                 
785                 /* add before or after the last channel? */
786                 if ((last->flag & REORDER_ISLAND_UNTOUCHABLE) == 0) {
787                         /* can add after it */
788                         BLI_addtail(list, island);
789                 }
790                 else {
791                         /* can at most go just before it, since last cannot be moved */
792                         BLI_insertlinkbefore(list, last, island);
793                         
794                 }
795                 
796                 return true;
797         }
798         
799         return false;
800 }
801
802 /* ............................. */
803
804 /**
805  * typedef for channel rearranging function
806  *
807  * \param list List of tReorderChannelIsland's that channels belong to
808  * \param island Island to be moved
809  * \return Whether operation was a success
810  */
811 typedef bool (*AnimChanRearrangeFp)(ListBase *list, tReorderChannelIsland *island);
812
813 /* get rearranging function, given 'rearrange' mode */
814 static AnimChanRearrangeFp rearrange_get_mode_func(eRearrangeAnimChan_Mode mode)
815 {
816         switch (mode) {
817                 case REARRANGE_ANIMCHAN_TOP:
818                         return rearrange_island_top;
819                 case REARRANGE_ANIMCHAN_UP:
820                         return rearrange_island_up;
821                 case REARRANGE_ANIMCHAN_DOWN:
822                         return rearrange_island_down;
823                 case REARRANGE_ANIMCHAN_BOTTOM:
824                         return rearrange_island_bottom;
825                 default:
826                         return NULL;
827         }
828 }
829
830 /* Rearrange Islands Generics ------------------------------------- */
831
832 /* add channel into list of islands */
833 static void rearrange_animchannel_add_to_islands(ListBase *islands, ListBase *srcList,
834                                                  Link *channel, eAnim_ChannelType type,
835                                                  const bool is_hidden)
836 {
837         tReorderChannelIsland *island = islands->last;  /* always try to add to last island if possible */
838         bool is_sel = false, is_untouchable = false;
839         
840         /* get flags - selected and untouchable from the channel */
841         switch (type) {
842                 case ANIMTYPE_GROUP:
843                 {
844                         bActionGroup *agrp = (bActionGroup *)channel;
845                         
846                         is_sel = SEL_AGRP(agrp);
847                         is_untouchable = (agrp->flag & AGRP_TEMP) != 0;
848                         break;
849                 }
850                 case ANIMTYPE_FCURVE:
851                 case ANIMTYPE_NLACURVE:
852                 {
853                         FCurve *fcu = (FCurve *)channel;
854                         
855                         is_sel = SEL_FCU(fcu);
856                         break;
857                 }
858                 case ANIMTYPE_NLATRACK:
859                 {
860                         NlaTrack *nlt = (NlaTrack *)channel;
861                         
862                         is_sel = SEL_NLT(nlt);
863                         break;
864                 }
865                 case ANIMTYPE_GPLAYER:
866                 {
867                         bGPDlayer *gpl = (bGPDlayer *)channel;
868                         
869                         is_sel = SEL_GPL(gpl);
870                         break;
871                 }
872                 default:
873                         printf("rearrange_animchannel_add_to_islands(): don't know how to handle channels of type %u\n", type);
874                         return;
875         }
876         
877         /* do we need to add to a new island? */
878         if (/* 1) no islands yet */
879             (island == NULL) ||
880             /* 2) unselected islands have single channels only - to allow up/down movement */
881             ((island->flag & REORDER_ISLAND_SELECTED) == 0) ||
882             /* 3) if channel is unselected, stop existing island (it was either wrong sel status, or full already) */
883             (is_sel == 0) ||
884             /* 4) hidden status changes */
885             ((island->flag & REORDER_ISLAND_HIDDEN) != is_hidden)
886             )
887         {
888                 /* create a new island now */
889                 island = MEM_callocN(sizeof(tReorderChannelIsland), "tReorderChannelIsland");
890                 BLI_addtail(islands, island);
891                 
892                 if (is_sel)
893                         island->flag |= REORDER_ISLAND_SELECTED;
894                 if (is_untouchable)
895                         island->flag |= REORDER_ISLAND_UNTOUCHABLE;
896                 if (is_hidden)
897                         island->flag |= REORDER_ISLAND_HIDDEN;
898         }
899
900         /* add channel to island - need to remove it from its existing list first though */
901         BLI_remlink(srcList, channel);
902         BLI_addtail(&island->channels, channel);
903 }
904
905 /* flatten islands out into a single list again */
906 static void rearrange_animchannel_flatten_islands(ListBase *islands, ListBase *srcList)
907 {
908         tReorderChannelIsland *island, *isn = NULL;
909         
910         /* make sure srcList is empty now */
911         BLI_assert(BLI_listbase_is_empty(srcList));
912         
913         /* go through merging islands */
914         for (island = islands->first; island; island = isn) {
915                 isn = island->next;
916                 
917                 /* merge island channels back to main list, then delete the island */
918                 BLI_movelisttolist(srcList, &island->channels);
919                 BLI_freelinkN(islands, island);
920         }
921 }
922
923 /* ............................. */
924
925 /* get a list of all bAnimListElem's of a certain type which are currently visible */
926 static void rearrange_animchannels_filter_visible(ListBase *anim_data_visible, bAnimContext *ac, eAnim_ChannelType type)
927 {
928         ListBase anim_data = {NULL, NULL};
929         bAnimListElem *ale, *ale_next;
930         int filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_LIST_CHANNELS);
931         
932         /* get all visible channels */
933         ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
934         
935         /* now, only keep the ones that are of the types we are interested in */
936         for (ale = anim_data.first; ale; ale = ale_next) {
937                 ale_next = ale->next;
938                 
939                 if (ale->type != type) {
940                         BLI_freelinkN(&anim_data, ale);
941                 }
942         }
943         
944         /* return cleaned up list */
945         *anim_data_visible = anim_data;
946 }
947
948 /* performing rearranging of channels using islands */
949 static bool rearrange_animchannel_islands(ListBase *list, AnimChanRearrangeFp rearrange_func,
950                                           eRearrangeAnimChan_Mode mode, eAnim_ChannelType type,
951                                           ListBase *anim_data_visible)
952 {
953         ListBase islands = {NULL, NULL};
954         Link *channel, *chanNext = NULL;
955         bool done = false;
956         
957         /* don't waste effort on an empty list */
958         if (BLI_listbase_is_empty(list))
959                 return 0;
960         
961         /* group channels into islands */
962         for (channel = list->first; channel; channel = chanNext) {
963                 /* find out whether this channel is present in anim_data_visible or not! */
964                 const bool is_hidden = (BLI_findptr(anim_data_visible, channel, offsetof(bAnimListElem, data)) == NULL);
965                 chanNext = channel->next;
966                 rearrange_animchannel_add_to_islands(&islands, list, channel, type, is_hidden);
967         }
968         
969         /* perform moving of selected islands now, but only if there is more than one of 'em so that something will happen 
970          *      - scanning of the list is performed in the opposite direction to the direction we're moving things, so that we 
971          *        shouldn't need to encounter items we've moved already
972          */
973         if (islands.first != islands.last) {
974                 tReorderChannelIsland *first = (mode > 0) ? islands.last : islands.first;
975                 tReorderChannelIsland *island, *isn = NULL;
976                 
977                 for (island = first; island; island = isn) {
978                         isn = (mode > 0) ? island->prev : island->next;
979                         
980                         /* perform rearranging */
981                         if (rearrange_func(&islands, island)) {
982                                 island->flag |= REORDER_ISLAND_MOVED;
983                                 done = true;
984                         }
985                 }
986         }
987         
988         /* ungroup islands */
989         rearrange_animchannel_flatten_islands(&islands, list);
990         
991         /* did we do anything? */
992         return done;
993 }
994
995 /* NLA Specific Stuff ----------------------------------------------------- */
996
997 /* Change the order NLA Tracks within NLA Stack
998  * ! NLA tracks are displayed in opposite order, so directions need care
999  *      mode: REARRANGE_ANIMCHAN_*  
1000  */
1001 static void rearrange_nla_channels(bAnimContext *ac, AnimData *adt, eRearrangeAnimChan_Mode mode)
1002 {
1003         AnimChanRearrangeFp rearrange_func;
1004         ListBase anim_data_visible = {NULL, NULL};
1005         
1006         /* hack: invert mode so that functions will work in right order */
1007         mode *= -1;
1008         
1009         /* get rearranging function */
1010         rearrange_func = rearrange_get_mode_func(mode);
1011         if (rearrange_func == NULL)
1012                 return;
1013         
1014         /* Filter visible data. */
1015         rearrange_animchannels_filter_visible(&anim_data_visible, ac, ANIMTYPE_NLATRACK);
1016         
1017         /* perform rearranging on tracks list */
1018         rearrange_animchannel_islands(&adt->nla_tracks, rearrange_func, mode, ANIMTYPE_NLATRACK, &anim_data_visible);
1019         
1020         /* free temp data */
1021         BLI_freelistN(&anim_data_visible);
1022 }
1023
1024 /* Drivers Specific Stuff ------------------------------------------------- */
1025
1026 /* Change the order drivers within AnimData block
1027  *      mode: REARRANGE_ANIMCHAN_*  
1028  */
1029 static void rearrange_driver_channels(bAnimContext *ac, AnimData *adt, eRearrangeAnimChan_Mode mode)
1030 {
1031         /* get rearranging function */
1032         AnimChanRearrangeFp rearrange_func = rearrange_get_mode_func(mode);
1033         ListBase anim_data_visible = {NULL, NULL};
1034         
1035         if (rearrange_func == NULL)
1036                 return;
1037         
1038         /* only consider drivers if they're accessible */
1039         if (EXPANDED_DRVD(adt) == 0)
1040                 return;
1041         
1042         /* Filter visible data. */
1043         rearrange_animchannels_filter_visible(&anim_data_visible, ac, ANIMTYPE_FCURVE);
1044         
1045         /* perform rearranging on drivers list (drivers are really just F-Curves) */
1046         rearrange_animchannel_islands(&adt->drivers, rearrange_func, mode, ANIMTYPE_FCURVE, &anim_data_visible);
1047         
1048         /* free temp data */
1049         BLI_freelistN(&anim_data_visible);
1050 }
1051
1052 /* Action Specific Stuff ------------------------------------------------- */
1053
1054 /* make sure all action-channels belong to a group (and clear action's list) */
1055 static void split_groups_action_temp(bAction *act, bActionGroup *tgrp)
1056 {
1057         bActionGroup *agrp;
1058         FCurve *fcu;
1059         
1060         if (act == NULL)
1061                 return;
1062         
1063         /* Separate F-Curves into lists per group */
1064         for (agrp = act->groups.first; agrp; agrp = agrp->next) {
1065                 if (agrp->channels.first) {
1066                         fcu = agrp->channels.last;
1067                         act->curves.first = fcu->next;
1068                         
1069                         fcu = agrp->channels.first;
1070                         fcu->prev = NULL;
1071                         
1072                         fcu = agrp->channels.last;
1073                         fcu->next = NULL;
1074                 }
1075         }
1076         
1077         /* Initialize memory for temp-group */
1078         memset(tgrp, 0, sizeof(bActionGroup));
1079         tgrp->flag |= (AGRP_EXPANDED | AGRP_TEMP);
1080         BLI_strncpy(tgrp->name, "#TempGroup", sizeof(tgrp->name));
1081         
1082         /* Move any action-channels not already moved, to the temp group */
1083         if (act->curves.first) {
1084                 /* start of list */
1085                 fcu = act->curves.first;
1086                 fcu->prev = NULL;
1087                 tgrp->channels.first = fcu;
1088                 act->curves.first = NULL;
1089                 
1090                 /* end of list */
1091                 fcu = act->curves.last;
1092                 fcu->next = NULL;
1093                 tgrp->channels.last = fcu;
1094                 act->curves.last = NULL;
1095                 
1096                 /* ensure that all of these get their group set to this temp group 
1097                  * (so that visibility filtering works)
1098                  */
1099                 for (fcu = tgrp->channels.first; fcu; fcu = fcu->next) {
1100                         fcu->grp = tgrp;
1101                 }
1102         }
1103         
1104         /* Add temp-group to list */
1105         BLI_addtail(&act->groups, tgrp);
1106 }
1107
1108 /* link lists of channels that groups have */
1109 static void join_groups_action_temp(bAction *act)
1110 {
1111         bActionGroup *agrp;
1112         
1113         for (agrp = act->groups.first; agrp; agrp = agrp->next) {
1114                 ListBase tempGroup;
1115                 
1116                 /* add list of channels to action's channels */
1117                 tempGroup = agrp->channels;
1118                 BLI_movelisttolist(&act->curves, &agrp->channels);
1119                 agrp->channels = tempGroup;
1120                 
1121                 /* clear moved flag */
1122                 agrp->flag &= ~AGRP_MOVED;
1123                 
1124                 /* if group was temporary one:
1125                  * - unassign all FCurves which were temporarily added to it
1126                  * - remove from list (but don't free as it's on the stack!)
1127                  */
1128                 if (agrp->flag & AGRP_TEMP) {
1129                         FCurve *fcu;
1130                         
1131                         for (fcu = agrp->channels.first; fcu; fcu = fcu->next) {
1132                                 fcu->grp = NULL;
1133                         }
1134                         
1135                         BLI_remlink(&act->groups, agrp);
1136                         break;
1137                 }
1138         }
1139 }
1140
1141 /* Change the order of anim-channels within action 
1142  *      mode: REARRANGE_ANIMCHAN_*  
1143  */
1144 static void rearrange_action_channels(bAnimContext *ac, bAction *act, eRearrangeAnimChan_Mode mode)
1145 {
1146         bActionGroup tgrp;
1147         ListBase anim_data_visible = {NULL, NULL};
1148         bool do_channels;
1149         
1150         /* get rearranging function */
1151         AnimChanRearrangeFp rearrange_func = rearrange_get_mode_func(mode);
1152         
1153         if (rearrange_func == NULL)
1154                 return;
1155         
1156         /* make sure we're only operating with groups (vs a mixture of groups+curves) */
1157         split_groups_action_temp(act, &tgrp);
1158         
1159         /* Filter visible data. */
1160         rearrange_animchannels_filter_visible(&anim_data_visible, ac, ANIMTYPE_GROUP);
1161         
1162         /* rearrange groups first 
1163          *      - the group's channels will only get considered if nothing happened when rearranging the groups
1164          *        i.e. the rearrange function returned 0
1165          */
1166         do_channels = (rearrange_animchannel_islands(&act->groups, rearrange_func, mode, ANIMTYPE_GROUP,
1167                                                      &anim_data_visible) == 0);
1168         
1169         /* free temp data */
1170         BLI_freelistN(&anim_data_visible);
1171         
1172         if (do_channels) {
1173                 bActionGroup *agrp;
1174                 
1175                 /* Filter visible data. */
1176                 rearrange_animchannels_filter_visible(&anim_data_visible, ac, ANIMTYPE_FCURVE);
1177                 
1178                 for (agrp = act->groups.first; agrp; agrp = agrp->next) {
1179                         /* only consider F-Curves if they're visible (group expanded) */
1180                         if (EXPANDED_AGRP(ac, agrp)) {
1181                                 rearrange_animchannel_islands(&agrp->channels, rearrange_func, mode, ANIMTYPE_FCURVE,
1182                                                               &anim_data_visible);
1183                         }
1184                 }
1185                 
1186                 /* free temp data */
1187                 BLI_freelistN(&anim_data_visible);
1188         }
1189         
1190         /* assemble lists into one list (and clear moved tags) */
1191         join_groups_action_temp(act);
1192 }
1193
1194 /* ------------------- */
1195
1196 static void rearrange_nla_control_channels(bAnimContext *ac, AnimData *adt, eRearrangeAnimChan_Mode mode)
1197 {
1198         ListBase anim_data_visible = {NULL, NULL};
1199         
1200         NlaTrack *nlt;
1201         NlaStrip *strip;
1202         
1203         /* get rearranging function */
1204         AnimChanRearrangeFp rearrange_func = rearrange_get_mode_func(mode);
1205         
1206         if (rearrange_func == NULL)
1207                 return;
1208                 
1209         /* skip if these curves aren't being shown */
1210         if (adt->flag & ADT_NLA_SKEYS_COLLAPSED)
1211                 return;
1212         
1213         /* Filter visible data. */
1214         rearrange_animchannels_filter_visible(&anim_data_visible, ac, ANIMTYPE_NLACURVE);
1215         
1216         /* we cannot rearrange between strips, but within each strip, we can rearrange those curves */
1217         for (nlt = adt->nla_tracks.first; nlt; nlt = nlt->next) {
1218                 for (strip = nlt->strips.first; strip; strip = strip->next) {
1219                         rearrange_animchannel_islands(&strip->fcurves, rearrange_func, mode, ANIMTYPE_NLACURVE,
1220                                                           &anim_data_visible);
1221                 }
1222         }
1223         
1224         /* free temp data */
1225         BLI_freelistN(&anim_data_visible);
1226 }
1227
1228 /* ------------------- */
1229
1230 static void rearrange_gpencil_channels(bAnimContext *ac, eRearrangeAnimChan_Mode mode)
1231 {
1232         ListBase anim_data = {NULL, NULL};
1233         bAnimListElem *ale;
1234         int filter;
1235         
1236         /* get rearranging function */
1237         AnimChanRearrangeFp rearrange_func = rearrange_get_mode_func(mode);
1238         
1239         if (rearrange_func == NULL)
1240                 return;
1241         
1242         /* get Grease Pencil datablocks */
1243         filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_ANIMDATA);
1244         ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
1245         
1246         for (ale = anim_data.first; ale; ale = ale->next) {
1247                 ListBase anim_data_visible = {NULL, NULL};
1248                 bGPdata *gpd = ale->data;
1249                 
1250                 /* only consider layers if this datablock is open */
1251                 BLI_assert(ale->type == ANIMTYPE_GPDATABLOCK);
1252                 if ((gpd->flag & GP_DATA_EXPAND) == 0)
1253                         continue;
1254                 
1255                 /* Filter visible data. */
1256                 rearrange_animchannels_filter_visible(&anim_data_visible, ac, ANIMTYPE_GPLAYER);
1257                 
1258                 /* rearrange datablock's layers */
1259                 rearrange_animchannel_islands(&gpd->layers, rearrange_func, mode, ANIMTYPE_GPLAYER, &anim_data_visible);
1260                 
1261                 /* free visible layers data */
1262                 BLI_freelistN(&anim_data_visible);
1263         }
1264         
1265         /* free GPD channel data */
1266         ANIM_animdata_freelist(&anim_data);
1267 }
1268
1269 /* ------------------- */
1270
1271 static int animchannels_rearrange_exec(bContext *C, wmOperator *op)
1272 {
1273         bAnimContext ac;
1274         eRearrangeAnimChan_Mode mode;
1275         
1276         /* get editor data */
1277         if (ANIM_animdata_get_context(C, &ac) == 0)
1278                 return OPERATOR_CANCELLED;
1279                 
1280         /* get mode */
1281         mode = RNA_enum_get(op->ptr, "direction");
1282         
1283         /* method to move channels depends on the editor */
1284         if (ac.datatype == ANIMCONT_GPENCIL) {
1285                 /* Grease Pencil channels */
1286                 rearrange_gpencil_channels(&ac, mode);
1287         }
1288         else if (ac.datatype == ANIMCONT_MASK) {
1289                 /* Grease Pencil channels */
1290                 printf("Mask does not supported for moving yet\n");
1291         }
1292         else if (ac.datatype == ANIMCONT_ACTION) {
1293                 /* Directly rearrange action's channels */
1294                 rearrange_action_channels(&ac, ac.data, mode);
1295         }
1296         else {
1297                 ListBase anim_data = {NULL, NULL};
1298                 bAnimListElem *ale;
1299                 int filter;
1300                 
1301                 /* get animdata blocks */
1302                 filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_ANIMDATA);
1303                 ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
1304                 
1305                 for (ale = anim_data.first; ale; ale = ale->next) {
1306                         AnimData *adt = ale->data;
1307                         
1308                         switch (ac.datatype) {
1309                                 case ANIMCONT_NLA: /* NLA-tracks only */
1310                                         rearrange_nla_channels(&ac, adt, mode);
1311                                         break;
1312                                 
1313                                 case ANIMCONT_DRIVERS: /* Drivers list only */
1314                                         rearrange_driver_channels(&ac, adt, mode);
1315                                         break;
1316                                 
1317                                 case ANIMCONT_ACTION: /* Single Action only... */
1318                                 case ANIMCONT_SHAPEKEY: // DOUBLE CHECK ME...
1319                                 {
1320                                         if (adt->action)
1321                                                 rearrange_action_channels(&ac, adt->action, mode);
1322                                         else if (G.debug & G_DEBUG)
1323                                                 printf("Animdata has no action\n");
1324                                         break;
1325                                 }
1326                                 
1327                                 default: /* DopeSheet/Graph Editor - Some Actions + NLA Control Curves */
1328                                 {
1329                                         /* NLA Control Curves */
1330                                         if (adt->nla_tracks.first)
1331                                                 rearrange_nla_control_channels(&ac, adt, mode);
1332                                         
1333                                         /* Action */
1334                                         if (adt->action)
1335                                                 rearrange_action_channels(&ac, adt->action, mode);
1336                                         else if (G.debug & G_DEBUG)
1337                                                 printf("Animdata has no action\n");
1338                                         break;
1339                                 }
1340                         }
1341                 }
1342                 
1343                 /* free temp data */
1344                 ANIM_animdata_freelist(&anim_data);
1345         }
1346         
1347         /* send notifier that things have changed */
1348         WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL);
1349         
1350         return OPERATOR_FINISHED;
1351 }
1352
1353 static void ANIM_OT_channels_move(wmOperatorType *ot)
1354 {
1355         /* identifiers */
1356         ot->name = "Move Channels";
1357         ot->idname = "ANIM_OT_channels_move";
1358         ot->description = "Rearrange selected animation channels";
1359         
1360         /* api callbacks */
1361         ot->exec = animchannels_rearrange_exec;
1362         ot->poll = animedit_poll_channels_nla_tweakmode_off;
1363         
1364         /* flags */
1365         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1366         
1367         /* props */
1368         ot->prop = RNA_def_enum(ot->srna, "direction", prop_animchannel_rearrange_types, REARRANGE_ANIMCHAN_DOWN, "Direction", "");
1369 }
1370
1371 /* ******************** Group Channel Operator ************************ */
1372
1373 static int animchannels_grouping_poll(bContext *C)
1374 {
1375         ScrArea *sa = CTX_wm_area(C);
1376         SpaceLink *sl;
1377
1378         /* channels region test */
1379         /* TODO: could enhance with actually testing if channels region? */
1380         if (ELEM(NULL, sa, CTX_wm_region(C)))
1381                 return 0;
1382                 
1383         /* animation editor test - must be suitable modes only */
1384         sl = CTX_wm_space_data(C);
1385         
1386         switch (sa->spacetype) {
1387                 /* supported... */
1388                 case SPACE_ACTION:
1389                 {
1390                         SpaceAction *saction = (SpaceAction *)sl;
1391                         
1392                         /* dopesheet and action only - all others are for other datatypes or have no groups */
1393                         if (ELEM(saction->mode, SACTCONT_ACTION, SACTCONT_DOPESHEET) == 0)
1394                                 return 0;
1395
1396                         break;
1397                 }
1398                 case SPACE_IPO:
1399                 {
1400                         SpaceIpo *sipo = (SpaceIpo *)sl;
1401                         
1402                         /* drivers can't have groups... */
1403                         if (sipo->mode != SIPO_MODE_ANIMATION)
1404                                 return 0;
1405
1406                         break;
1407                 }
1408                 /* unsupported... */
1409                 default:
1410                         return 0;
1411         }
1412         
1413         return 1;
1414 }
1415
1416 /* ----------------------------------------------------------- */
1417
1418 static void animchannels_group_channels(bAnimContext *ac, bAnimListElem *adt_ref, const char name[])
1419 {       
1420         AnimData *adt = adt_ref->adt;
1421         bAction *act = adt->action;
1422         
1423         if (act) {
1424                 ListBase anim_data = {NULL, NULL};
1425                 int filter;
1426                 
1427                 /* find selected F-Curves to re-group */
1428                 filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_SEL);
1429                 ANIM_animdata_filter(ac, &anim_data, filter, adt_ref, ANIMCONT_CHANNEL);
1430                 
1431                 if (anim_data.first) {
1432                         bActionGroup *agrp;
1433                         bAnimListElem *ale;
1434                         
1435                         /* create new group, which should now be part of the action */
1436                         agrp = action_groups_add_new(act, name);
1437                         BLI_assert(agrp != NULL);
1438                         
1439                         /* transfer selected F-Curves across to new group  */
1440                         for (ale = anim_data.first; ale; ale = ale->next) {
1441                                 FCurve *fcu = (FCurve *)ale->data;
1442                                 bActionGroup *grp = fcu->grp;
1443                                 
1444                                 /* remove F-Curve from group, then group too if it is now empty */
1445                                 action_groups_remove_channel(act, fcu);
1446                                 
1447                                 if ((grp) && BLI_listbase_is_empty(&grp->channels)) {
1448                                         BLI_freelinkN(&act->groups, grp);
1449                                 }
1450                                 
1451                                 /* add F-Curve to group */
1452                                 action_groups_add_channel(act, agrp, fcu);
1453                         }
1454                 }
1455                 
1456                 /* cleanup */
1457                 ANIM_animdata_freelist(&anim_data);
1458         }
1459 }
1460
1461 static int animchannels_group_exec(bContext *C, wmOperator *op)
1462 {
1463         bAnimContext ac;
1464         char name[MAX_NAME];
1465         
1466         /* get editor data */
1467         if (ANIM_animdata_get_context(C, &ac) == 0)
1468                 return OPERATOR_CANCELLED;
1469         
1470         /* get name for new group */
1471         RNA_string_get(op->ptr, "name", name);
1472         
1473         /* XXX: name for group should never be empty... */
1474         if (name[0]) {
1475                 ListBase anim_data = {NULL, NULL};
1476                 bAnimListElem *ale;
1477                 int filter;
1478                 
1479                 /* handle each animdata block separately, so that the regrouping doesn't flow into blocks  */
1480                 filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_ANIMDATA | ANIMFILTER_NODUPLIS);
1481                 ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
1482                 
1483                 for (ale = anim_data.first; ale; ale = ale->next) {
1484                         animchannels_group_channels(&ac, ale, name);
1485                 }
1486                 
1487                 /* free temp data */
1488                 ANIM_animdata_freelist(&anim_data);
1489                 
1490                 /* updatss */
1491                 WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL);
1492         }
1493         
1494         return OPERATOR_FINISHED;
1495 }
1496
1497 static void ANIM_OT_channels_group(wmOperatorType *ot)
1498 {
1499         /* identifiers */
1500         ot->name = "Group Channels";
1501         ot->idname = "ANIM_OT_channels_group";
1502         ot->description = "Add selected F-Curves to a new group";
1503         
1504         /* callbacks */
1505         ot->invoke = WM_operator_props_popup;
1506         ot->exec = animchannels_group_exec;
1507         ot->poll = animchannels_grouping_poll;
1508         
1509         /* flags */
1510         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1511         
1512         /* props */
1513         ot->prop = RNA_def_string(ot->srna, "name", "New Group", 
1514                                   sizeof(((bActionGroup *)NULL)->name), 
1515                                   "Name", "Name of newly created group");
1516         /* RNA_def_property_flag(ot->prop, PROP_SKIP_SAVE); */ /* XXX: still not too sure about this - keeping same text is confusing... */
1517 }
1518
1519 /* ----------------------------------------------------------- */
1520
1521 static int animchannels_ungroup_exec(bContext *C, wmOperator *UNUSED(op))
1522 {
1523         bAnimContext ac;
1524         
1525         ListBase anim_data = {NULL, NULL};
1526         bAnimListElem *ale;
1527         int filter;
1528         
1529         /* get editor data */
1530         if (ANIM_animdata_get_context(C, &ac) == 0)
1531                 return OPERATOR_CANCELLED;
1532                 
1533         /* just selected F-Curves... */
1534         filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_SEL | ANIMFILTER_NODUPLIS);
1535         ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
1536         
1537         for (ale = anim_data.first; ale; ale = ale->next) {
1538                 /* find action for this F-Curve... */
1539                 if (ale->adt && ale->adt->action) {
1540                         FCurve  *fcu = (FCurve *)ale->data;
1541                         bAction *act = ale->adt->action;
1542                         
1543                         /* only proceed to remove if F-Curve is in a group... */
1544                         if (fcu->grp) { 
1545                                 bActionGroup *agrp = fcu->grp;
1546                                 
1547                                 /* remove F-Curve from group and add at tail (ungrouped) */
1548                                 action_groups_remove_channel(act, fcu);
1549                                 BLI_addtail(&act->curves, fcu);
1550                                 
1551                                 /* delete group if it is now empty */
1552                                 if (BLI_listbase_is_empty(&agrp->channels)) {
1553                                         BLI_freelinkN(&act->groups, agrp);
1554                                 }
1555                         }
1556                 }
1557         }
1558         
1559         /* cleanup */
1560         ANIM_animdata_freelist(&anim_data);
1561         
1562         /* updates */
1563         WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL);
1564         
1565         return OPERATOR_FINISHED;
1566 }
1567
1568 static void ANIM_OT_channels_ungroup(wmOperatorType *ot)
1569 {
1570         /* identifiers */
1571         ot->name = "Ungroup Channels";
1572         ot->idname = "ANIM_OT_channels_ungroup";
1573         ot->description = "Remove selected F-Curves from their current groups";
1574         
1575         /* callbacks */
1576         ot->exec = animchannels_ungroup_exec;
1577         ot->poll = animchannels_grouping_poll;
1578         
1579         /* flags */
1580         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1581 }
1582
1583 /* ******************** Delete Channel Operator *********************** */
1584
1585 static int animchannels_delete_exec(bContext *C, wmOperator *UNUSED(op))
1586 {
1587         bAnimContext ac;
1588         ListBase anim_data = {NULL, NULL};
1589         bAnimListElem *ale;
1590         int filter;
1591         
1592         /* get editor data */
1593         if (ANIM_animdata_get_context(C, &ac) == 0)
1594                 return OPERATOR_CANCELLED;
1595         
1596         /* cannot delete in shapekey */
1597         if (ac.datatype == ANIMCONT_SHAPEKEY) 
1598                 return OPERATOR_CANCELLED;
1599                 
1600                 
1601         /* do groups only first (unless in Drivers mode, where there are none) */
1602         if (ac.datatype != ANIMCONT_DRIVERS) {
1603                 /* filter data */
1604                 filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_SEL | ANIMFILTER_LIST_CHANNELS | ANIMFILTER_FOREDIT | ANIMFILTER_NODUPLIS);
1605                 ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
1606                 
1607                 /* delete selected groups and their associated channels */
1608                 for (ale = anim_data.first; ale; ale = ale->next) {
1609                         /* only groups - don't check other types yet, since they may no-longer exist */
1610                         if (ale->type == ANIMTYPE_GROUP) {
1611                                 bActionGroup *agrp = (bActionGroup *)ale->data;
1612                                 AnimData *adt = ale->adt;
1613                                 FCurve *fcu, *fcn;
1614                                 
1615                                 /* skip this group if no AnimData available, as we can't safely remove the F-Curves */
1616                                 if (adt == NULL)
1617                                         continue;
1618                                 
1619                                 /* delete all of the Group's F-Curves, but no others */
1620                                 for (fcu = agrp->channels.first; fcu && fcu->grp == agrp; fcu = fcn) {
1621                                         fcn = fcu->next;
1622                                         
1623                                         /* remove from group and action, then free */
1624                                         action_groups_remove_channel(adt->action, fcu);
1625                                         free_fcurve(fcu);
1626                                 }
1627                                 
1628                                 /* free the group itself */
1629                                 if (adt->action)
1630                                         BLI_freelinkN(&adt->action->groups, agrp);
1631                                 else
1632                                         MEM_freeN(agrp);
1633                         }
1634                 }
1635                 
1636                 /* cleanup */
1637                 ANIM_animdata_freelist(&anim_data);
1638         }
1639         
1640         /* filter data */
1641         filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_SEL | ANIMFILTER_FOREDIT | ANIMFILTER_NODUPLIS);
1642         ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
1643         
1644         /* delete selected data channels */
1645         for (ale = anim_data.first; ale; ale = ale->next) {
1646                 switch (ale->type) {
1647                         case ANIMTYPE_FCURVE: 
1648                         {
1649                                 /* F-Curves if we can identify its parent */
1650                                 AnimData *adt = ale->adt;
1651                                 FCurve *fcu = (FCurve *)ale->data;
1652                                 
1653                                 /* try to free F-Curve */
1654                                 ANIM_fcurve_delete_from_animdata(&ac, adt, fcu);
1655                                 break;
1656                         }
1657                         case ANIMTYPE_NLACURVE:
1658                         {
1659                                 /* NLA Control Curve - Deleting it should disable the corresponding setting... */
1660                                 NlaStrip *strip = (NlaStrip *)ale->owner;
1661                                 FCurve *fcu = (FCurve *)ale->data;
1662                                 
1663                                 if (STREQ(fcu->rna_path, "strip_time")) {
1664                                         strip->flag &= ~NLASTRIP_FLAG_USR_TIME;
1665                                 }
1666                                 else if (STREQ(fcu->rna_path, "influence")) {
1667                                         strip->flag &= ~NLASTRIP_FLAG_USR_INFLUENCE;
1668                                 }
1669                                 else {
1670                                         printf("ERROR: Trying to delete NLA Control Curve for unknown property '%s'\n", fcu->rna_path);
1671                                 }
1672                                 
1673                                 /* unlink and free the F-Curve */
1674                                 BLI_remlink(&strip->fcurves, fcu);
1675                                 free_fcurve(fcu);
1676                                 break;
1677                         }
1678                         case ANIMTYPE_GPLAYER:
1679                         {
1680                                 /* Grease Pencil layer */
1681                                 bGPdata *gpd = (bGPdata *)ale->id;
1682                                 bGPDlayer *gpl = (bGPDlayer *)ale->data;
1683                                 
1684                                 /* try to delete the layer's data and the layer itself */
1685                                 free_gpencil_frames(gpl);
1686                                 BLI_freelinkN(&gpd->layers, gpl);
1687                                 break;
1688                         }
1689                         case ANIMTYPE_MASKLAYER:
1690                         {
1691                                 /* Mask layer */
1692                                 Mask *mask = (Mask *)ale->id;
1693                                 MaskLayer *masklay = (MaskLayer *)ale->data;
1694                                 
1695                                 /* try to delete the layer's data and the layer itself */
1696                                 BKE_mask_layer_remove(mask, masklay);
1697                                 break;
1698                         }
1699                 }
1700         }
1701         
1702         /* cleanup */
1703         ANIM_animdata_freelist(&anim_data);
1704         
1705         /* send notifier that things have changed */
1706         WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL);
1707         DAG_relations_tag_update(CTX_data_main(C));
1708
1709         return OPERATOR_FINISHED;
1710 }
1711  
1712 static void ANIM_OT_channels_delete(wmOperatorType *ot)
1713 {
1714         /* identifiers */
1715         ot->name = "Delete Channels";
1716         ot->idname = "ANIM_OT_channels_delete";
1717         ot->description = "Delete all selected animation channels";
1718         
1719         /* api callbacks */
1720         ot->exec = animchannels_delete_exec;
1721         ot->poll = animedit_poll_channels_active;
1722         
1723         /* flags */
1724         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1725 }
1726
1727 /* ********************** Set Flags Operator *********************** */
1728
1729 /* defines for setting animation-channel flags */
1730 static EnumPropertyItem prop_animchannel_setflag_types[] = {
1731         {ACHANNEL_SETFLAG_TOGGLE, "TOGGLE", 0, "Toggle", ""},
1732         {ACHANNEL_SETFLAG_CLEAR, "DISABLE", 0, "Disable", ""},
1733         {ACHANNEL_SETFLAG_ADD, "ENABLE", 0, "Enable", ""},
1734         {ACHANNEL_SETFLAG_INVERT, "INVERT", 0, "Invert", ""},
1735         {0, NULL, 0, NULL, NULL}
1736 };
1737
1738 /* defines for set animation-channel settings */
1739 // TODO: could add some more types, but those are really quite dependent on the mode...
1740 static EnumPropertyItem prop_animchannel_settings_types[] = {
1741         {ACHANNEL_SETTING_PROTECT, "PROTECT", 0, "Protect", ""},
1742         {ACHANNEL_SETTING_MUTE, "MUTE", 0, "Mute", ""},
1743         {0, NULL, 0, NULL, NULL}
1744 };
1745
1746
1747 /* ------------------- */
1748
1749 /* Set/clear a particular flag (setting) for all selected + visible channels 
1750  *      setting: the setting to modify
1751  *      mode: eAnimChannels_SetFlag
1752  *      onlysel: only selected channels get the flag set
1753  */
1754 // TODO: enable a setting which turns flushing on/off?
1755 static void setflag_anim_channels(bAnimContext *ac, eAnimChannel_Settings setting, eAnimChannels_SetFlag mode, bool onlysel, bool flush)
1756 {
1757         ListBase anim_data = {NULL, NULL};
1758         ListBase all_data = {NULL, NULL};
1759         bAnimListElem *ale;
1760         int filter;
1761         
1762         /* filter data that we need if flush is on */
1763         if (flush) {
1764                 /* get list of all channels that selection may need to be flushed to 
1765                  * - hierarchy visibility needs to be ignored so that settings can get flushed
1766                  *   "down" inside closed containers
1767                  */
1768                 filter = ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_CHANNELS;
1769                 ANIM_animdata_filter(ac, &all_data, filter, ac->data, ac->datatype);
1770         }
1771         
1772         /* filter data that we're working on 
1773          * - hierarchy matters if we're doing this from the channels region
1774          *   since we only want to apply this to channels we can "see", 
1775          *   and have these affect their relatives
1776          * - but for Graph Editor, this gets used also from main region
1777          *   where hierarchy doesn't apply [#21276]
1778          */
1779         if ((ac->spacetype == SPACE_IPO) && (ac->regiontype != RGN_TYPE_CHANNELS)) {
1780                 /* graph editor (case 2) */
1781                 filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_CHANNELS | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_NODUPLIS);
1782         }
1783         else {
1784                 /* standard case */
1785                 filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_LIST_CHANNELS | ANIMFILTER_NODUPLIS);
1786         }
1787         if (onlysel) filter |= ANIMFILTER_SEL;
1788         ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
1789         
1790         /* if toggling, check if disable or enable */
1791         if (mode == ACHANNEL_SETFLAG_TOGGLE) {
1792                 /* default to turn all on, unless we encounter one that's on... */
1793                 mode = ACHANNEL_SETFLAG_ADD;
1794                 
1795                 /* see if we should turn off instead... */
1796                 for (ale = anim_data.first; ale; ale = ale->next) {
1797                         /* set the setting in the appropriate way (if available) */
1798                         if (ANIM_channel_setting_get(ac, ale, setting) > 0) {
1799                                 mode = ACHANNEL_SETFLAG_CLEAR;
1800                                 break;
1801                         }
1802                 }
1803         }
1804         
1805         /* apply the setting */
1806         for (ale = anim_data.first; ale; ale = ale->next) {
1807                 /* skip channel if setting is not available */
1808                 if (ANIM_channel_setting_get(ac, ale, setting) == -1)
1809                         continue;
1810                 
1811                 /* set the setting in the appropriate way */
1812                 ANIM_channel_setting_set(ac, ale, setting, mode);
1813                 
1814                 /* if flush status... */
1815                 if (flush)
1816                         ANIM_flush_setting_anim_channels(ac, &all_data, ale, setting, mode);
1817         }
1818         
1819         ANIM_animdata_freelist(&anim_data);
1820         BLI_freelistN(&all_data);
1821 }
1822
1823 /* ------------------- */
1824
1825 static int animchannels_setflag_exec(bContext *C, wmOperator *op)
1826 {
1827         bAnimContext ac;
1828         eAnimChannel_Settings setting;
1829         eAnimChannels_SetFlag mode;
1830         bool flush = true;
1831         
1832         /* get editor data */
1833         if (ANIM_animdata_get_context(C, &ac) == 0)
1834                 return OPERATOR_CANCELLED;
1835                 
1836         /* mode (eAnimChannels_SetFlag), setting (eAnimChannel_Settings) */
1837         mode = RNA_enum_get(op->ptr, "mode");
1838         setting = RNA_enum_get(op->ptr, "type");
1839         
1840         /* check if setting is flushable */
1841         if (setting == ACHANNEL_SETTING_EXPAND)
1842                 flush = false;
1843         
1844         /* modify setting 
1845          *      - only selected channels are affected
1846          */
1847         setflag_anim_channels(&ac, setting, mode, true, flush);
1848         
1849         /* send notifier that things have changed */
1850         WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL);
1851         
1852         return OPERATOR_FINISHED;
1853 }
1854
1855 /* duplicate of 'ANIM_OT_channels_setting_toggle' for menu title only, weak! */
1856 static void ANIM_OT_channels_setting_enable(wmOperatorType *ot)
1857 {
1858         PropertyRNA *prop;
1859         
1860         /* identifiers */
1861         ot->name = "Enable Channel Setting";
1862         ot->idname = "ANIM_OT_channels_setting_enable";
1863         ot->description = "Enable specified setting on all selected animation channels";
1864         
1865         /* api callbacks */
1866         ot->invoke = WM_menu_invoke;
1867         ot->exec = animchannels_setflag_exec;
1868         ot->poll = animedit_poll_channels_active;
1869         
1870         /* flags */
1871         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1872         
1873         /* props */
1874         /* flag-setting mode */
1875         prop = RNA_def_enum(ot->srna, "mode", prop_animchannel_setflag_types, ACHANNEL_SETFLAG_ADD, "Mode", "");
1876         RNA_def_property_flag(prop, PROP_HIDDEN);
1877         /* setting to set */
1878         ot->prop = RNA_def_enum(ot->srna, "type", prop_animchannel_settings_types, 0, "Type", "");
1879 }
1880 /* duplicate of 'ANIM_OT_channels_setting_toggle' for menu title only, weak! */
1881 static void ANIM_OT_channels_setting_disable(wmOperatorType *ot)
1882 {
1883         PropertyRNA *prop;
1884         
1885         /* identifiers */
1886         ot->name = "Disable Channel Setting";
1887         ot->idname = "ANIM_OT_channels_setting_disable";
1888         ot->description = "Disable specified setting on all selected animation channels";
1889         
1890         /* api callbacks */
1891         ot->invoke = WM_menu_invoke;
1892         ot->exec = animchannels_setflag_exec;
1893         ot->poll = animedit_poll_channels_active;
1894         
1895         /* flags */
1896         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1897         
1898         /* props */
1899         /* flag-setting mode */
1900         prop = RNA_def_enum(ot->srna, "mode", prop_animchannel_setflag_types, ACHANNEL_SETFLAG_CLEAR, "Mode", "");
1901         RNA_def_property_flag(prop, PROP_HIDDEN); /* internal hack - don't expose */
1902         /* setting to set */
1903         ot->prop = RNA_def_enum(ot->srna, "type", prop_animchannel_settings_types, 0, "Type", "");
1904 }
1905
1906 static void ANIM_OT_channels_setting_toggle(wmOperatorType *ot)
1907 {
1908         PropertyRNA *prop;
1909         
1910         /* identifiers */
1911         ot->name = "Toggle Channel Setting";
1912         ot->idname = "ANIM_OT_channels_setting_toggle";
1913         ot->description = "Toggle specified setting on all selected animation channels";
1914         
1915         /* api callbacks */
1916         ot->invoke = WM_menu_invoke;
1917         ot->exec = animchannels_setflag_exec;
1918         ot->poll = animedit_poll_channels_active;
1919         
1920         /* flags */
1921         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1922         
1923         /* props */
1924         /* flag-setting mode */
1925         prop = RNA_def_enum(ot->srna, "mode", prop_animchannel_setflag_types, ACHANNEL_SETFLAG_TOGGLE, "Mode", "");
1926         RNA_def_property_flag(prop, PROP_HIDDEN); /* internal hack - don't expose */
1927         /* setting to set */
1928         ot->prop = RNA_def_enum(ot->srna, "type", prop_animchannel_settings_types, 0, "Type", "");
1929 }
1930
1931 static void ANIM_OT_channels_editable_toggle(wmOperatorType *ot)
1932 {
1933         PropertyRNA *prop;
1934         
1935         /* identifiers */
1936         ot->name = "Toggle Channel Editability";
1937         ot->idname = "ANIM_OT_channels_editable_toggle";
1938         ot->description = "Toggle editability of selected channels";
1939         
1940         /* api callbacks */
1941         ot->exec = animchannels_setflag_exec;
1942         ot->poll = animedit_poll_channels_active;
1943         
1944         /* flags */
1945         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1946         
1947         /* props */
1948         /* flag-setting mode */
1949         RNA_def_enum(ot->srna, "mode", prop_animchannel_setflag_types, ACHANNEL_SETFLAG_TOGGLE, "Mode", "");
1950         /* setting to set */
1951         prop = RNA_def_enum(ot->srna, "type", prop_animchannel_settings_types, ACHANNEL_SETTING_PROTECT, "Type", "");
1952         RNA_def_property_flag(prop, PROP_HIDDEN); /* internal hack - don't expose */
1953 }
1954
1955 /* ********************** Expand Channels Operator *********************** */
1956
1957 static int animchannels_expand_exec(bContext *C, wmOperator *op)
1958 {
1959         bAnimContext ac;
1960         bool onlysel = true;
1961         
1962         /* get editor data */
1963         if (ANIM_animdata_get_context(C, &ac) == 0)
1964                 return OPERATOR_CANCELLED;
1965                 
1966         /* only affect selected channels? */
1967         if (RNA_boolean_get(op->ptr, "all"))
1968                 onlysel = false;
1969         
1970         /* modify setting */
1971         setflag_anim_channels(&ac, ACHANNEL_SETTING_EXPAND, ACHANNEL_SETFLAG_ADD, onlysel, false);
1972         
1973         /* send notifier that things have changed */
1974         WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL);
1975         
1976         return OPERATOR_FINISHED;
1977 }
1978
1979 static void ANIM_OT_channels_expand(wmOperatorType *ot)
1980 {
1981         /* identifiers */
1982         ot->name = "Expand Channels";
1983         ot->idname = "ANIM_OT_channels_expand";
1984         ot->description = "Expand (i.e. open) all selected expandable animation channels";
1985         
1986         /* api callbacks */
1987         ot->exec = animchannels_expand_exec;
1988         ot->poll = animedit_poll_channels_active;
1989         
1990         /* flags */
1991         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1992         
1993         /* props */
1994         ot->prop = RNA_def_boolean(ot->srna, "all", 1, "All", "Expand all channels (not just selected ones)");
1995 }
1996
1997 /* ********************** Collapse Channels Operator *********************** */
1998
1999 static int animchannels_collapse_exec(bContext *C, wmOperator *op)
2000 {
2001         bAnimContext ac;
2002         bool onlysel = true;
2003         
2004         /* get editor data */
2005         if (ANIM_animdata_get_context(C, &ac) == 0)
2006                 return OPERATOR_CANCELLED;
2007                 
2008         /* only affect selected channels? */
2009         if (RNA_boolean_get(op->ptr, "all"))
2010                 onlysel = false;
2011         
2012         /* modify setting */
2013         setflag_anim_channels(&ac, ACHANNEL_SETTING_EXPAND, ACHANNEL_SETFLAG_CLEAR, onlysel, false);
2014         
2015         /* send notifier that things have changed */
2016         WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL);
2017         
2018         return OPERATOR_FINISHED;
2019 }
2020
2021 static void ANIM_OT_channels_collapse(wmOperatorType *ot)
2022 {
2023         /* identifiers */
2024         ot->name = "Collapse Channels";
2025         ot->idname = "ANIM_OT_channels_collapse";
2026         ot->description = "Collapse (i.e. close) all selected expandable animation channels";
2027         
2028         /* api callbacks */
2029         ot->exec = animchannels_collapse_exec;
2030         ot->poll = animedit_poll_channels_active;
2031         
2032         /* flags */
2033         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2034         
2035         /* props */
2036         ot->prop = RNA_def_boolean(ot->srna, "all", true, "All", "Collapse all channels (not just selected ones)");
2037 }
2038
2039 /* ************ Remove All "Empty" AnimData Blocks Operator ********* */
2040 /* We define "empty" AnimData blocks here as those which have all 3 of criteria:
2041  *  1) No active action OR that active actions are empty
2042  *     Assuming that all legitimate entries will have an action,
2043  *     and that empty actions
2044  *  2) No NLA Tracks + NLA Strips
2045  *     Assuming that users haven't set up any of these as "placeholders"
2046  *     for convenience sake, and that most that exist were either unintentional
2047  *     or are no longer wanted
2048  *  3) No drivers
2049  */
2050  
2051 static int animchannels_clean_empty_exec(bContext *C, wmOperator *UNUSED(op))
2052 {
2053         bAnimContext ac;
2054         
2055         ListBase anim_data = {NULL, NULL};
2056         bAnimListElem *ale;
2057         int filter;
2058         
2059         /* get editor data */
2060         if (ANIM_animdata_get_context(C, &ac) == 0)
2061                 return OPERATOR_CANCELLED;
2062         
2063         /* get animdata blocks */
2064         filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_ANIMDATA);
2065         ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
2066         
2067         for (ale = anim_data.first; ale; ale = ale->next) {
2068                 ID *id = ale->id;
2069                 AnimData *adt = ale->data;
2070                 
2071                 bool action_empty  = false;
2072                 bool nla_empty     = false;
2073                 bool drivers_empty = false;
2074                 
2075                 /* sanity checks */
2076                 BLI_assert((id != NULL) && (adt != NULL));
2077                 
2078                 /* check if this is "empty" and can be deleted */
2079                 /* (For now, there are only these 3 criteria) */
2080                 
2081                 /* 1) Active Action is missing or empty */
2082                 if (ELEM(NULL, adt->action, adt->action->curves.first)) {
2083                         action_empty = true;
2084                 }
2085                 else {
2086                         /* TODO: check for keyframe + fmodifier data on these too */
2087                 }
2088                 
2089                 /* 2) No NLA Tracks and/or NLA Strips */
2090                 if (adt->nla_tracks.first == NULL) {
2091                         nla_empty = true;
2092                 }
2093                 else {
2094                         NlaTrack *nlt;
2095                         
2096                         /* empty tracks? */
2097                         for (nlt = adt->nla_tracks.first; nlt; nlt = nlt->next) {
2098                                 if (nlt->strips.first) {
2099                                         /* stop searching, as we found one that actually had stuff we don't want lost 
2100                                          * NOTE: nla_empty gets reset to false, as a previous track may have been empty
2101                                          */
2102                                         nla_empty = false;
2103                                         break;
2104                                 }
2105                                 else if (nlt->strips.first == NULL) {
2106                                         /* this track is empty, but another one may still have stuff in it, so can't break yet */
2107                                         nla_empty = true;
2108                                 }
2109                         }
2110                 }
2111                 
2112                 /* 3) Drivers */
2113                 drivers_empty = (adt->drivers.first == NULL);
2114                 
2115                 
2116                 /* remove AnimData? */
2117                 if (action_empty && nla_empty && drivers_empty) {
2118                         BKE_animdata_free(id);
2119                 }
2120         }
2121         
2122         /* free temp data */
2123         ANIM_animdata_freelist(&anim_data);
2124         
2125         /* send notifier that things have changed */
2126         WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL);
2127         
2128         return OPERATOR_FINISHED;
2129 }
2130
2131 static void ANIM_OT_channels_clean_empty(wmOperatorType *ot)
2132 {
2133         /* identifiers */
2134         ot->name = "Remove Empty Animation Data";
2135         ot->idname = "ANIM_OT_channels_clean_empty";
2136         ot->description = "Delete all empty animation data containers from visible datablocks";
2137         
2138         /* api callbacks */
2139         ot->exec = animchannels_clean_empty_exec;
2140         ot->poll = animedit_poll_channels_nla_tweakmode_off;
2141         
2142         /* flags */
2143         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2144 }
2145
2146 /* ******************* Reenable Disabled Operator ******************* */
2147
2148 static int animchannels_enable_poll(bContext *C)
2149 {
2150         ScrArea *sa = CTX_wm_area(C);
2151         
2152         /* channels region test */
2153         /* TODO: could enhance with actually testing if channels region? */
2154         if (ELEM(NULL, sa, CTX_wm_region(C)))
2155                 return 0;
2156                 
2157         /* animation editor test - Action/Dopesheet/etc. and Graph only */
2158         if (ELEM(sa->spacetype, SPACE_ACTION, SPACE_IPO) == 0)
2159                 return 0;
2160                 
2161         return 1;
2162 }
2163
2164 static int animchannels_enable_exec(bContext *C, wmOperator *UNUSED(op))
2165 {
2166         bAnimContext ac;
2167         
2168         ListBase anim_data = {NULL, NULL};
2169         bAnimListElem *ale;
2170         int filter;
2171         
2172         /* get editor data */
2173         if (ANIM_animdata_get_context(C, &ac) == 0)
2174                 return OPERATOR_CANCELLED;
2175         
2176         /* filter data */
2177         filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_NODUPLIS);
2178         ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
2179         
2180         /* loop through filtered data and clean curves */
2181         for (ale = anim_data.first; ale; ale = ale->next) {
2182                 FCurve *fcu = (FCurve *)ale->data;
2183                 
2184                 /* remove disabled flags from F-Curves */
2185                 fcu->flag &= ~FCURVE_DISABLED;
2186                 
2187                 /* for drivers, let's do the same too */
2188                 if (fcu->driver)
2189                         fcu->driver->flag &= ~DRIVER_FLAG_INVALID;
2190                         
2191                 /* tag everything for updates - in particular, this is needed to get drivers working again */
2192                 ale->update |= ANIM_UPDATE_DEPS;
2193         }
2194         
2195         ANIM_animdata_update(&ac, &anim_data);
2196         ANIM_animdata_freelist(&anim_data);
2197                 
2198         /* send notifier that things have changed */
2199         WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL);
2200         
2201         return OPERATOR_FINISHED;
2202 }
2203
2204 static void ANIM_OT_channels_fcurves_enable(wmOperatorType *ot)
2205 {
2206         /* identifiers */
2207         ot->name = "Revive Disabled F-Curves";
2208         ot->idname = "ANIM_OT_channels_fcurves_enable";
2209         ot->description = "Clears 'disabled' tag from all F-Curves to get broken F-Curves working again";
2210         
2211         /* api callbacks */
2212         ot->exec = animchannels_enable_exec;
2213         ot->poll = animchannels_enable_poll;
2214         
2215         /* flags */
2216         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2217 }
2218
2219 /* ****************** Find / Set Filter Operator ******************** */
2220
2221 /* XXX: make this generic? */
2222 static int animchannels_find_poll(bContext *C)
2223 {
2224         ScrArea *sa = CTX_wm_area(C);
2225         
2226         if (sa == NULL)
2227                 return 0;
2228         
2229         /* animation editor with dopesheet */
2230         return ELEM(sa->spacetype, SPACE_ACTION, SPACE_IPO, SPACE_NLA);
2231 }
2232
2233 /* find_invoke() - Get initial channels */
2234 static int animchannels_find_invoke(bContext *C, wmOperator *op, const wmEvent *evt)
2235 {
2236         bAnimContext ac;
2237         
2238         /* get editor data */
2239         if (ANIM_animdata_get_context(C, &ac) == 0)
2240                 return OPERATOR_CANCELLED;
2241         
2242         /* set initial filter text, and enable filter */
2243         RNA_string_set(op->ptr, "query", ac.ads->searchstr);
2244         
2245         /* defer to popup */
2246         return WM_operator_props_popup(C, op, evt);
2247 }
2248
2249 /* find_exec() -  Called to set the value */
2250 static int animchannels_find_exec(bContext *C, wmOperator *op)
2251 {
2252         bAnimContext ac;
2253         
2254         /* get editor data */
2255         if (ANIM_animdata_get_context(C, &ac) == 0)
2256                 return OPERATOR_CANCELLED;
2257         
2258         /* update filter text, and ensure that filter is enabled if there's something there
2259          * NOTE: we turn the filter off if there's nothing (this is a quick shortcut for dismissing)
2260          */
2261         RNA_string_get(op->ptr, "query", ac.ads->searchstr);
2262         
2263         if (ac.ads->searchstr[0]) {
2264                 ac.ads->filterflag |= ADS_FILTER_BY_FCU_NAME;
2265         }
2266         else {
2267                 ac.ads->filterflag &= ~ADS_FILTER_BY_FCU_NAME;
2268         }
2269         
2270         /* redraw */
2271         WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL);
2272         
2273         return OPERATOR_FINISHED;
2274 }
2275
2276 static void ANIM_OT_channels_find(wmOperatorType *ot)
2277 {
2278         /* identifiers */
2279         ot->name = "Find Channels";
2280         ot->idname = "ANIM_OT_channels_find";
2281         ot->description = "Filter the set of channels shown to only include those with matching names";
2282         
2283         /* callbacks */
2284         ot->invoke = animchannels_find_invoke;
2285         ot->exec = animchannels_find_exec;
2286         ot->poll = animchannels_find_poll;
2287         
2288         /* flags */
2289         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2290         
2291         /* properties */
2292         ot->prop = RNA_def_string(ot->srna, "query", "Query", sizeof(((bDopeSheet *)NULL)->searchstr), "", "Text to search for in channel names");
2293 }
2294
2295 /* ********************** Select All Operator *********************** */
2296
2297 static int animchannels_deselectall_exec(bContext *C, wmOperator *op)
2298 {
2299         bAnimContext ac;
2300         
2301         /* get editor data */
2302         if (ANIM_animdata_get_context(C, &ac) == 0)
2303                 return OPERATOR_CANCELLED;
2304                 
2305         /* 'standard' behavior - check if selected, then apply relevant selection */
2306         if (RNA_boolean_get(op->ptr, "invert"))
2307                 ANIM_deselect_anim_channels(&ac, ac.data, ac.datatype, false, ACHANNEL_SETFLAG_INVERT);
2308         else
2309                 ANIM_deselect_anim_channels(&ac, ac.data, ac.datatype, true, ACHANNEL_SETFLAG_ADD);
2310         
2311         /* send notifier that things have changed */
2312         WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_SELECTED, NULL);
2313         
2314         return OPERATOR_FINISHED;
2315 }
2316  
2317 static void ANIM_OT_channels_select_all_toggle(wmOperatorType *ot)
2318 {
2319         /* identifiers */
2320         ot->name = "Select All";
2321         ot->idname = "ANIM_OT_channels_select_all_toggle";
2322         ot->description = "Toggle selection of all animation channels";
2323         
2324         /* api callbacks */
2325         ot->exec = animchannels_deselectall_exec;
2326         ot->poll = animedit_poll_channels_nla_tweakmode_off;
2327         
2328         /* flags */
2329         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2330         
2331         /* props */
2332         ot->prop = RNA_def_boolean(ot->srna, "invert", false, "Invert", "");
2333 }
2334
2335 /* ******************** Borderselect Operator *********************** */
2336
2337 static void borderselect_anim_channels(bAnimContext *ac, rcti *rect, short selectmode)
2338 {
2339         ListBase anim_data = {NULL, NULL};
2340         bAnimListElem *ale;
2341         int filter;
2342         
2343         SpaceNla *snla = (SpaceNla *)ac->sl;
2344         View2D *v2d = &ac->ar->v2d;
2345         rctf rectf;
2346         float ymin, ymax;
2347         
2348         /* set initial y extents */
2349         if (ac->datatype == ANIMCONT_NLA) {
2350                 ymin = (float)(-NLACHANNEL_HEIGHT(snla));
2351                 ymax = 0.0f;
2352         }
2353         else {
2354                 ymin = 0.0f;
2355                 ymax = (float)(-ACHANNEL_HEIGHT);
2356         }
2357         
2358         /* convert border-region to view coordinates */
2359         UI_view2d_region_to_view(v2d, rect->xmin, rect->ymin + 2, &rectf.xmin, &rectf.ymin);
2360         UI_view2d_region_to_view(v2d, rect->xmax, rect->ymax - 2, &rectf.xmax, &rectf.ymax);
2361         
2362         /* filter data */
2363         filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_LIST_CHANNELS);
2364         ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
2365         
2366         /* loop over data, doing border select */
2367         for (ale = anim_data.first; ale; ale = ale->next) {
2368                 if (ac->datatype == ANIMCONT_NLA)
2369                         ymin = ymax - NLACHANNEL_STEP(snla);
2370                 else
2371                         ymin = ymax - ACHANNEL_STEP;
2372                 
2373                 /* if channel is within border-select region, alter it */
2374                 if (!((ymax < rectf.ymin) || (ymin > rectf.ymax))) {
2375                         /* set selection flags only */
2376                         ANIM_channel_setting_set(ac, ale, ACHANNEL_SETTING_SELECT, selectmode);
2377                         
2378                         /* type specific actions */
2379                         switch (ale->type) {
2380                                 case ANIMTYPE_GROUP:
2381                                 {
2382                                         bActionGroup *agrp = (bActionGroup *)ale->data;
2383                                         
2384                                         /* Armatures-Specific Feature:
2385                                          * See mouse_anim_channels() -> ANIMTYPE_GROUP case for more details (T38737)
2386                                          */
2387                                         if ((ac->ads->filterflag & ADS_FILTER_ONLYSEL) == 0) {
2388                                                 if ((ale->id) && (GS(ale->id->name) == ID_OB)) {
2389                                                         Object *ob = (Object *)ale->id;
2390                                                         
2391                                                         if (ob->type == OB_ARMATURE) {
2392                                                                 /* Assume for now that any group with corresponding name is what we want
2393                                                                  * (i.e. for an armature whose location is animated, things would break
2394                                                                  * if the user were to add a bone named "Location").
2395                                                                  *
2396                                                                  * TODO: check the first F-Curve or so to be sure...
2397                                                                  */
2398                                                                 bPoseChannel *pchan = BKE_pose_channel_find_name(ob->pose, agrp->name);
2399                                                                 
2400                                                                 if (agrp->flag & AGRP_SELECTED) {
2401                                                                         ED_pose_bone_select(ob, pchan, true);
2402                                                                 }
2403                                                                 else {
2404                                                                         ED_pose_bone_select(ob, pchan, false);
2405                                                                 }
2406                                                         }
2407                                                 }
2408                                         }
2409                                         
2410                                         /* always clear active flag after doing this */
2411                                         agrp->flag &= ~AGRP_ACTIVE;
2412                                         break;
2413                                 }
2414                                 case ANIMTYPE_NLATRACK:
2415                                 {
2416                                         NlaTrack *nlt = (NlaTrack *)ale->data;
2417                                         
2418                                         /* for now, it's easier just to do this here manually, as defining a new type 
2419                                          * currently adds complications when doing other stuff 
2420                                          */
2421                                         ACHANNEL_SET_FLAG(nlt, selectmode, NLATRACK_SELECTED);
2422                                         break;
2423                                 }
2424                         }
2425                 }
2426                 
2427                 /* set minimum extent to be the maximum of the next channel */
2428                 ymax = ymin;
2429         }
2430         
2431         /* cleanup */
2432         ANIM_animdata_freelist(&anim_data);
2433 }
2434
2435 /* ------------------- */
2436
2437 static int animchannels_borderselect_exec(bContext *C, wmOperator *op)
2438 {
2439         bAnimContext ac;
2440         rcti rect;
2441         short selectmode = 0;
2442         int gesture_mode;
2443         bool extend;
2444         
2445         /* get editor data */
2446         if (ANIM_animdata_get_context(C, &ac) == 0)
2447                 return OPERATOR_CANCELLED;
2448         
2449         /* get settings from operator */
2450         WM_operator_properties_border_to_rcti(op, &rect);
2451         
2452         gesture_mode = RNA_int_get(op->ptr, "gesture_mode");
2453         extend = RNA_boolean_get(op->ptr, "extend");
2454
2455         if (!extend)
2456                 ANIM_deselect_anim_channels(&ac, ac.data, ac.datatype, true, ACHANNEL_SETFLAG_CLEAR);
2457
2458         if (gesture_mode == GESTURE_MODAL_SELECT)
2459                 selectmode = ACHANNEL_SETFLAG_ADD;
2460         else
2461                 selectmode = ACHANNEL_SETFLAG_CLEAR;
2462         
2463         /* apply borderselect animation channels */
2464         borderselect_anim_channels(&ac, &rect, selectmode);
2465         
2466         /* send notifier that things have changed */
2467         WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_SELECTED, NULL);
2468         
2469         return OPERATOR_FINISHED;
2470
2471
2472 static void ANIM_OT_channels_select_border(wmOperatorType *ot)
2473 {
2474         /* identifiers */
2475         ot->name = "Border Select";
2476         ot->idname = "ANIM_OT_channels_select_border";
2477         ot->description = "Select all animation channels within the specified region";
2478         
2479         /* api callbacks */
2480         ot->invoke = WM_border_select_invoke;
2481         ot->exec = animchannels_borderselect_exec;
2482         ot->modal = WM_border_select_modal;
2483         ot->cancel = WM_border_select_cancel;
2484         
2485         ot->poll = animedit_poll_channels_nla_tweakmode_off;
2486         
2487         /* flags */
2488         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2489         
2490         /* rna */
2491         WM_operator_properties_gesture_border(ot, true);
2492 }
2493
2494 /* ******************* Rename Operator ***************************** */
2495 /* Allow renaming some channels by clicking on them */
2496
2497 static bool rename_anim_channels(bAnimContext *ac, int channel_index)
2498 {
2499         ListBase anim_data = {NULL, NULL};
2500         const bAnimChannelType *acf;
2501         bAnimListElem *ale;
2502         int filter;
2503         bool success = false;
2504         
2505         /* get the channel that was clicked on */
2506         /* filter channels */
2507         filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_LIST_CHANNELS);
2508         ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
2509         
2510         /* get channel from index */
2511         ale = BLI_findlink(&anim_data, channel_index);
2512         if (ale == NULL) {
2513                 /* channel not found */
2514                 if (G.debug & G_DEBUG)
2515                         printf("Error: animation channel (index = %d) not found in rename_anim_channels()\n", channel_index);
2516                 
2517                 ANIM_animdata_freelist(&anim_data);
2518                 return false;
2519         }
2520         
2521         /* check that channel can be renamed */
2522         acf = ANIM_channel_get_typeinfo(ale);
2523         if (acf && acf->name_prop) {
2524                 PointerRNA ptr;
2525                 PropertyRNA *prop;
2526                 
2527                 /* ok if we can get name property to edit from this channel */
2528                 if (acf->name_prop(ale, &ptr, &prop)) {
2529                         /* actually showing the rename textfield is done on redraw,
2530                          * so here we just store the index of this channel in the 
2531                          * dopesheet data, which will get utilized when drawing the
2532                          * channel...
2533                          *
2534                          * +1 factor is for backwards compat issues
2535                          */
2536                         if (ac->ads) {
2537                                 ac->ads->renameIndex = channel_index + 1;
2538                                 success = true;
2539                         }
2540                 }
2541         }
2542         
2543         /* free temp data and tag for refresh */
2544         ANIM_animdata_freelist(&anim_data);
2545         ED_region_tag_redraw(ac->ar);
2546         return success;
2547 }
2548
2549 static int animchannels_channel_get(bAnimContext *ac, const int mval[2])
2550 {
2551         ARegion *ar;
2552         View2D *v2d;
2553         int channel_index;
2554         float x, y;
2555         
2556         /* get useful pointers from animation context data */
2557         ar = ac->ar;
2558         v2d = &ar->v2d;
2559         
2560         /* figure out which channel user clicked in 
2561          * Note: although channels technically start at (y = ACHANNEL_FIRST), we need to adjust by half a channel's height
2562          *              so that the tops of channels get caught ok. Since ACHANNEL_FIRST is really ACHANNEL_HEIGHT, we simply use
2563          *              ACHANNEL_HEIGHT_HALF.
2564          */
2565         UI_view2d_region_to_view(v2d, mval[0], mval[1], &x, &y);
2566         
2567         if (ac->datatype == ANIMCONT_NLA) {
2568                 SpaceNla *snla = (SpaceNla *)ac->sl;
2569                 UI_view2d_listview_view_to_cell(v2d, NLACHANNEL_NAMEWIDTH, NLACHANNEL_STEP(snla), 0, (float)NLACHANNEL_HEIGHT_HALF(snla), x, y, NULL, &channel_index);
2570         }
2571         else {
2572                 UI_view2d_listview_view_to_cell(v2d, ACHANNEL_NAMEWIDTH, ACHANNEL_STEP, 0, (float)ACHANNEL_HEIGHT_HALF, x, y, NULL, &channel_index);
2573         }
2574
2575         return channel_index;
2576 }
2577
2578 static int animchannels_rename_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *event)
2579 {
2580         bAnimContext ac;
2581         int channel_index;
2582
2583         /* get editor data */
2584         if (ANIM_animdata_get_context(C, &ac) == 0)
2585                 return OPERATOR_CANCELLED;
2586
2587         channel_index = animchannels_channel_get(&ac, event->mval);
2588
2589         /* handle click */
2590         if (rename_anim_channels(&ac, channel_index))
2591                 return OPERATOR_FINISHED;
2592         else
2593                 /* allow event to be handled by selectall operator */
2594                 return OPERATOR_PASS_THROUGH;
2595 }
2596
2597 static void ANIM_OT_channels_rename(wmOperatorType *ot)
2598 {
2599         /* identifiers */
2600         ot->name = "Rename Channels";
2601         ot->idname = "ANIM_OT_channels_rename";
2602         ot->description = "Rename animation channel under mouse";
2603         
2604         /* api callbacks */
2605         ot->invoke = animchannels_rename_invoke;
2606         ot->poll = animedit_poll_channels_active;
2607 }
2608
2609 /* ******************** Mouse-Click Operator *********************** */
2610 /* Handle selection changes due to clicking on channels. Settings will get caught by UI code... */
2611
2612 static int mouse_anim_channels(bContext *C, bAnimContext *ac, int channel_index, short selectmode)
2613 {
2614         ListBase anim_data = {NULL, NULL};
2615         bAnimListElem *ale;
2616         int filter;
2617         int notifierFlags = 0;
2618         
2619         /* get the channel that was clicked on */
2620         /* filter channels */
2621         filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_LIST_CHANNELS);
2622         ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
2623         
2624         /* get channel from index */
2625         ale = BLI_findlink(&anim_data, channel_index);
2626         if (ale == NULL) {
2627                 /* channel not found */
2628                 if (G.debug & G_DEBUG)
2629                         printf("Error: animation channel (index = %d) not found in mouse_anim_channels()\n", channel_index);
2630                 
2631                 ANIM_animdata_freelist(&anim_data);
2632                 return 0;
2633         }
2634
2635         /* selectmode -1 is a special case for ActionGroups only, which selects all of the channels underneath it only... */
2636         /* TODO: should this feature be extended to work with other channel types too? */
2637         if ((selectmode == -1) && (ale->type != ANIMTYPE_GROUP)) {
2638                 /* normal channels should not behave normally in this case */
2639                 ANIM_animdata_freelist(&anim_data);
2640                 return 0;
2641         }
2642
2643         /* action to take depends on what channel we've got */
2644         /* WARNING: must keep this in sync with the equivalent function in nla_channels.c */
2645         switch (ale->type) {
2646                 case ANIMTYPE_SCENE:
2647                 {
2648                         Scene *sce = (Scene *)ale->data;
2649                         AnimData *adt = sce->adt;
2650                         
2651                         /* set selection status */
2652                         if (selectmode == SELECT_INVERT) {
2653                                 /* swap select */
2654                                 sce->flag ^= SCE_DS_SELECTED;
2655                                 if (adt) adt->flag ^= ADT_UI_SELECTED;
2656                         }
2657                         else {
2658                                 sce->flag |= SCE_DS_SELECTED;
2659                                 if (adt) adt->flag |= ADT_UI_SELECTED;
2660                         }
2661                         
2662                         notifierFlags |= (ND_ANIMCHAN | NA_SELECTED);
2663                         break;
2664                 }
2665                 case ANIMTYPE_OBJECT:
2666                 {
2667                         bDopeSheet *ads = (bDopeSheet *)ac->data;
2668                         Scene *sce = (Scene *)ads->source;
2669                         Base *base = (Base *)ale->data;
2670                         Object *ob = base->object;
2671                         AnimData *adt = ob->adt;
2672                         
2673                         /* set selection status */
2674                         if (selectmode == SELECT_INVERT) {
2675                                 /* swap select */
2676                                 base->flag ^= SELECT;
2677                                 ob->flag = base->flag;
2678                                 
2679                                 if (adt) adt->flag ^= ADT_UI_SELECTED;
2680                         }
2681                         else {
2682                                 Base *b;
2683                                 
2684                                 /* deselect all */
2685                                 /* TODO: should this deselect all other types of channels too? */
2686                                 for (b = sce->base.first; b; b = b->next) {
2687                                         b->flag &= ~SELECT;
2688                                         b->object->flag = b->flag;
2689                                         if (b->object->adt) b->object->adt->flag &= ~(ADT_UI_SELECTED | ADT_UI_ACTIVE);
2690                                 }
2691                                 
2692                                 /* select object now */
2693                                 base->flag |= SELECT;
2694                                 ob->flag |= SELECT;
2695                                 if (adt) adt->flag |= ADT_UI_SELECTED;
2696                         }
2697                         
2698                         /* change active object - regardless of whether it is now selected [T37883] */
2699                         ED_base_object_activate(C, base); /* adds notifier */
2700                         
2701                         if ((adt) && (adt->flag & ADT_UI_SELECTED))
2702                                 adt->flag |= ADT_UI_ACTIVE;
2703                         
2704                         notifierFlags |= (ND_ANIMCHAN | NA_SELECTED);
2705                         break;
2706                 }
2707                 case ANIMTYPE_FILLACTD: /* Action Expander */
2708                 case ANIMTYPE_DSMAT:    /* Datablock AnimData Expanders */
2709                 case ANIMTYPE_DSLAM:
2710                 case ANIMTYPE_DSCAM:
2711                 case ANIMTYPE_DSCUR:
2712                 case ANIMTYPE_DSSKEY:
2713                 case ANIMTYPE_DSWOR:
2714                 case ANIMTYPE_DSMBALL:
2715                 case ANIMTYPE_DSARM:
2716                 case ANIMTYPE_DSMESH:
2717                 case ANIMTYPE_DSNTREE:
2718                 case ANIMTYPE_DSTEX:
2719                 case ANIMTYPE_DSLAT:
2720                 case ANIMTYPE_DSLINESTYLE:
2721                 case ANIMTYPE_DSSPK:
2722                 case ANIMTYPE_DSGPENCIL:
2723                 {
2724                         /* sanity checking... */
2725                         if (ale->adt) {
2726                                 /* select/deselect */
2727                                 if (selectmode == SELECT_INVERT) {
2728                                         /* inverse selection status of this AnimData block only */
2729                                         ale->adt->flag ^= ADT_UI_SELECTED;
2730                                 }
2731                                 else {
2732                                         /* select AnimData block by itself */
2733                                         ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, false, ACHANNEL_SETFLAG_CLEAR);
2734                                         ale->adt->flag |= ADT_UI_SELECTED;
2735                                 }
2736                                 
2737                                 /* set active? */
2738                                 if ((ale->adt) && (ale->adt->flag & ADT_UI_SELECTED))
2739                                         ale->adt->flag |= ADT_UI_ACTIVE;
2740                         }
2741                         
2742                         notifierFlags |= (ND_ANIMCHAN | NA_SELECTED);
2743                         break;
2744                 }
2745                 case ANIMTYPE_GROUP: 
2746                 {
2747                         bActionGroup *agrp = (bActionGroup *)ale->data;
2748                         
2749                         Object *ob = NULL;
2750                         bPoseChannel *pchan = NULL;
2751                         
2752                         
2753                         /* Armatures-Specific Feature:
2754                          * Since groups are used to collect F-Curves of the same Bone by default
2755                          * (via Keying Sets) so that they can be managed better, we try to make
2756                          * things here easier for animators by mapping group selection to bone
2757                          * selection. 
2758                          *
2759                          * Only do this if "Only Selected" dopesheet filter is not active, or else it
2760                          * becomes too unpredictable/tricky to manage
2761                          */
2762                         if ((ac->ads->filterflag & ADS_FILTER_ONLYSEL) == 0) {
2763                                 if ((ale->id) && (GS(ale->id->name) == ID_OB)) {
2764                                         ob = (Object *)ale->id;
2765                                         
2766                                         if (ob->type == OB_ARMATURE) {
2767                                                 /* Assume for now that any group with corresponding name is what we want
2768                                                  * (i.e. for an armature whose location is animated, things would break
2769                                                  * if the user were to add a bone named "Location").
2770                                                  *
2771                                                  * TODO: check the first F-Curve or so to be sure...
2772                                                  */
2773                                                 pchan = BKE_pose_channel_find_name(ob->pose, agrp->name);
2774                                         }       
2775                                 }
2776                         }
2777                         
2778                         /* select/deselect group */
2779                         if (selectmode == SELECT_INVERT) {
2780                                 /* inverse selection status of this group only */
2781                                 agrp->flag ^= AGRP_SELECTED;
2782                         }
2783                         else if (selectmode == -1) {
2784                                 /* select all in group (and deselect everthing else) */
2785                                 FCurve *fcu;
2786                                 
2787                                 /* deselect all other channels */
2788                                 ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, false, ACHANNEL_SETFLAG_CLEAR);
2789                                 if (pchan) ED_pose_de_selectall(ob, SEL_DESELECT, false);
2790                                 
2791                                 /* only select channels in group and group itself */
2792                                 for (fcu = agrp->channels.first; fcu && fcu->grp == agrp; fcu = fcu->next)
2793                                         fcu->flag |= FCURVE_SELECTED;
2794                                 agrp->flag |= AGRP_SELECTED;
2795                         }
2796                         else {
2797                                 /* select group by itself */
2798                                 ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, false, ACHANNEL_SETFLAG_CLEAR);
2799                                 if (pchan) ED_pose_de_selectall(ob, SEL_DESELECT, false);
2800                                 
2801                                 agrp->flag |= AGRP_SELECTED;
2802                         }
2803                         
2804                         /* if group is selected now, make group the 'active' one in the visible list */
2805                         if (agrp->flag & AGRP_SELECTED) {
2806                                 ANIM_set_active_channel(ac, ac->data, ac->datatype, filter, agrp, ANIMTYPE_GROUP);
2807                                 if (pchan) ED_pose_bone_select(ob, pchan, true);
2808                         }
2809                         else {
2810                                 ANIM_set_active_channel(ac, ac->data, ac->datatype, filter, NULL, ANIMTYPE_GROUP);
2811                                 if (pchan) ED_pose_bone_select(ob, pchan, false);
2812                         }
2813                                 
2814                         notifierFlags |= (ND_ANIMCHAN | NA_SELECTED);
2815                         break;
2816                 }
2817                 case ANIMTYPE_FCURVE:
2818                 case ANIMTYPE_NLACURVE:
2819                 {
2820                         FCurve *fcu = (FCurve *)ale->data;
2821                         
2822                         /* select/deselect */
2823                         if (selectmode == SELECT_INVERT) {
2824                                 /* inverse selection status of this F-Curve only */
2825                                 fcu->flag ^= FCURVE_SELECTED;
2826                         }
2827                         else {
2828                                 /* select F-Curve by itself */
2829                                 ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, false, ACHANNEL_SETFLAG_CLEAR);
2830                                 fcu->flag |= FCURVE_SELECTED;
2831                         }
2832                         
2833                         /* if F-Curve is selected now, make F-Curve the 'active' one in the visible list */
2834                         if (fcu->flag & FCURVE_SELECTED)
2835                                 ANIM_set_active_channel(ac, ac->data, ac->datatype, filter, fcu, ale->type);
2836                                 
2837                         notifierFlags |= (ND_ANIMCHAN | NA_SELECTED);
2838                         break;
2839                 }
2840                 case ANIMTYPE_SHAPEKEY: 
2841                 {
2842                         KeyBlock *kb = (KeyBlock *)ale->data;
2843                         
2844                         /* select/deselect */
2845                         if (selectmode == SELECT_INVERT) {
2846                                 /* inverse selection status of this ShapeKey only */
2847                                 kb->flag ^= KEYBLOCK_SEL;
2848                         }
2849                         else {
2850                                 /* select ShapeKey by itself */
2851                                 ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, false, ACHANNEL_SETFLAG_CLEAR);
2852                                 kb->flag |= KEYBLOCK_SEL;
2853                         }
2854                                 
2855                         notifierFlags |= (ND_ANIMCHAN | NA_SELECTED);
2856                         break;
2857                 }
2858                 case ANIMTYPE_NLACONTROLS:
2859                 {
2860                         AnimData *adt = (AnimData *)ale->data;
2861                         
2862                         /* toggle expand
2863                          *   - Although the triangle widget already allows this, since there's nothing else that can be done here now,
2864                          *     let's just use it for easier expand/collapse for now
2865                          */
2866                         adt->flag ^= ADT_NLA_SKEYS_COLLAPSED;
2867                         
2868                         notifierFlags |= (ND_ANIMCHAN | NA_EDITED);
2869                         break;
2870                 }
2871                 case ANIMTYPE_GPDATABLOCK:
2872                 {
2873                         bGPdata *gpd = (bGPdata *)ale->data;
2874                         
2875                         /* toggle expand 
2876                          *      - although the triangle widget already allows this, the whole channel can also be used for this purpose
2877                          */
2878                         gpd->flag ^= GP_DATA_EXPAND;
2879                         
2880                         notifierFlags |= (ND_ANIMCHAN | NA_EDITED);
2881                         break;
2882                 }
2883                 case ANIMTYPE_GPLAYER:
2884                 {
2885                         bGPDlayer *gpl = (bGPDlayer *)ale->data;
2886                         
2887                         /* select/deselect */
2888                         if (selectmode == SELECT_INVERT) {
2889                                 /* invert selection status of this layer only */
2890                                 gpl->flag ^= GP_LAYER_SELECT;
2891                         }
2892                         else {