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