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