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