7ee1b8cbf9c6d0e905d5dbc80bccea6237db1d02
[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 Flags Operator *********************** */
1589
1590 /* defines for setting animation-channel flags */
1591 static EnumPropertyItem prop_animchannel_setflag_types[] = {
1592         {ACHANNEL_SETFLAG_TOGGLE, "TOGGLE", 0, "Toggle", ""},
1593         {ACHANNEL_SETFLAG_CLEAR, "DISABLE", 0, "Disable", ""},
1594         {ACHANNEL_SETFLAG_ADD, "ENABLE", 0, "Enable", ""},
1595         {ACHANNEL_SETFLAG_INVERT, "INVERT", 0, "Invert", ""},
1596         {0, NULL, 0, NULL, NULL}
1597 };
1598
1599 /* defines for set animation-channel settings */
1600 // TODO: could add some more types, but those are really quite dependent on the mode...
1601 static EnumPropertyItem prop_animchannel_settings_types[] = {
1602         {ACHANNEL_SETTING_PROTECT, "PROTECT", 0, "Protect", ""},
1603         {ACHANNEL_SETTING_MUTE, "MUTE", 0, "Mute", ""},
1604         {0, NULL, 0, NULL, NULL}
1605 };
1606
1607
1608 /* ------------------- */
1609
1610 /* Set/clear a particular flag (setting) for all selected + visible channels 
1611  *      setting: the setting to modify
1612  *      mode: eAnimChannels_SetFlag
1613  *      onlysel: only selected channels get the flag set
1614  */
1615 // TODO: enable a setting which turns flushing on/off?
1616 static void setflag_anim_channels(bAnimContext *ac, eAnimChannel_Settings setting, eAnimChannels_SetFlag mode, bool onlysel, bool flush)
1617 {
1618         ListBase anim_data = {NULL, NULL};
1619         ListBase all_data = {NULL, NULL};
1620         bAnimListElem *ale;
1621         int filter;
1622         
1623         /* filter data that we need if flush is on */
1624         if (flush) {
1625                 /* get list of all channels that selection may need to be flushed to 
1626                  * - hierarchy visibility needs to be ignored so that settings can get flushed
1627                  *   "down" inside closed containers
1628                  */
1629                 filter = ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_CHANNELS;
1630                 ANIM_animdata_filter(ac, &all_data, filter, ac->data, ac->datatype);
1631         }
1632         
1633         /* filter data that we're working on 
1634          * - hierarchy matters if we're doing this from the channels region
1635          *   since we only want to apply this to channels we can "see", 
1636          *   and have these affect their relatives
1637          * - but for Graph Editor, this gets used also from main region
1638          *   where hierarchy doesn't apply [#21276]
1639          */
1640         if ((ac->spacetype == SPACE_IPO) && (ac->regiontype != RGN_TYPE_CHANNELS)) {
1641                 /* graph editor (case 2) */
1642                 filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_CHANNELS | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_NODUPLIS);
1643         }
1644         else {
1645                 /* standard case */
1646                 filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_LIST_CHANNELS | ANIMFILTER_NODUPLIS);
1647         }
1648         if (onlysel) filter |= ANIMFILTER_SEL;
1649         ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
1650         
1651         /* if toggling, check if disable or enable */
1652         if (mode == ACHANNEL_SETFLAG_TOGGLE) {
1653                 /* default to turn all on, unless we encounter one that's on... */
1654                 mode = ACHANNEL_SETFLAG_ADD;
1655                 
1656                 /* see if we should turn off instead... */
1657                 for (ale = anim_data.first; ale; ale = ale->next) {
1658                         /* set the setting in the appropriate way (if available) */
1659                         if (ANIM_channel_setting_get(ac, ale, setting) > 0) {
1660                                 mode = ACHANNEL_SETFLAG_CLEAR;
1661                                 break;
1662                         }
1663                 }
1664         }
1665         
1666         /* apply the setting */
1667         for (ale = anim_data.first; ale; ale = ale->next) {
1668                 /* skip channel if setting is not available */
1669                 if (ANIM_channel_setting_get(ac, ale, setting) == -1)
1670                         continue;
1671                 
1672                 /* set the setting in the appropriate way */
1673                 ANIM_channel_setting_set(ac, ale, setting, mode);
1674                 
1675                 /* if flush status... */
1676                 if (flush)
1677                         ANIM_flush_setting_anim_channels(ac, &all_data, ale, setting, mode);
1678         }
1679         
1680         ANIM_animdata_freelist(&anim_data);
1681         BLI_freelistN(&all_data);
1682 }
1683
1684 /* ------------------- */
1685
1686 static int animchannels_setflag_exec(bContext *C, wmOperator *op)
1687 {
1688         bAnimContext ac;
1689         eAnimChannel_Settings setting;
1690         eAnimChannels_SetFlag mode;
1691         bool flush = true;
1692         
1693         /* get editor data */
1694         if (ANIM_animdata_get_context(C, &ac) == 0)
1695                 return OPERATOR_CANCELLED;
1696                 
1697         /* mode (eAnimChannels_SetFlag), setting (eAnimChannel_Settings) */
1698         mode = RNA_enum_get(op->ptr, "mode");
1699         setting = RNA_enum_get(op->ptr, "type");
1700         
1701         /* check if setting is flushable */
1702         if (setting == ACHANNEL_SETTING_EXPAND)
1703                 flush = false;
1704         
1705         /* modify setting 
1706          *      - only selected channels are affected
1707          */
1708         setflag_anim_channels(&ac, setting, mode, true, flush);
1709         
1710         /* send notifier that things have changed */
1711         WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL);
1712         
1713         return OPERATOR_FINISHED;
1714 }
1715
1716 /* duplicate of 'ANIM_OT_channels_setting_toggle' for menu title only, weak! */
1717 static void ANIM_OT_channels_setting_enable(wmOperatorType *ot)
1718 {
1719         PropertyRNA *prop;
1720         
1721         /* identifiers */
1722         ot->name = "Enable Channel Setting";
1723         ot->idname = "ANIM_OT_channels_setting_enable";
1724         ot->description = "Enable specified setting on all selected animation channels";
1725         
1726         /* api callbacks */
1727         ot->invoke = WM_menu_invoke;
1728         ot->exec = animchannels_setflag_exec;
1729         ot->poll = animedit_poll_channels_active;
1730         
1731         /* flags */
1732         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1733         
1734         /* props */
1735         /* flag-setting mode */
1736         prop = RNA_def_enum(ot->srna, "mode", prop_animchannel_setflag_types, ACHANNEL_SETFLAG_ADD, "Mode", "");
1737         RNA_def_property_flag(prop, PROP_HIDDEN);
1738         /* setting to set */
1739         ot->prop = RNA_def_enum(ot->srna, "type", prop_animchannel_settings_types, 0, "Type", "");
1740 }
1741 /* duplicate of 'ANIM_OT_channels_setting_toggle' for menu title only, weak! */
1742 static void ANIM_OT_channels_setting_disable(wmOperatorType *ot)
1743 {
1744         PropertyRNA *prop;
1745         
1746         /* identifiers */
1747         ot->name = "Disable Channel Setting";
1748         ot->idname = "ANIM_OT_channels_setting_disable";
1749         ot->description = "Disable specified setting on all selected animation channels";
1750         
1751         /* api callbacks */
1752         ot->invoke = WM_menu_invoke;
1753         ot->exec = animchannels_setflag_exec;
1754         ot->poll = animedit_poll_channels_active;
1755         
1756         /* flags */
1757         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1758         
1759         /* props */
1760         /* flag-setting mode */
1761         prop = RNA_def_enum(ot->srna, "mode", prop_animchannel_setflag_types, ACHANNEL_SETFLAG_CLEAR, "Mode", "");
1762         RNA_def_property_flag(prop, PROP_HIDDEN); /* internal hack - don't expose */
1763         /* setting to set */
1764         ot->prop = RNA_def_enum(ot->srna, "type", prop_animchannel_settings_types, 0, "Type", "");
1765 }
1766
1767 static void ANIM_OT_channels_setting_toggle(wmOperatorType *ot)
1768 {
1769         PropertyRNA *prop;
1770         
1771         /* identifiers */
1772         ot->name = "Toggle Channel Setting";
1773         ot->idname = "ANIM_OT_channels_setting_toggle";
1774         ot->description = "Toggle specified setting on all selected animation channels";
1775         
1776         /* api callbacks */
1777         ot->invoke = WM_menu_invoke;
1778         ot->exec = animchannels_setflag_exec;
1779         ot->poll = animedit_poll_channels_active;
1780         
1781         /* flags */
1782         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1783         
1784         /* props */
1785         /* flag-setting mode */
1786         prop = RNA_def_enum(ot->srna, "mode", prop_animchannel_setflag_types, ACHANNEL_SETFLAG_TOGGLE, "Mode", "");
1787         RNA_def_property_flag(prop, PROP_HIDDEN); /* internal hack - don't expose */
1788         /* setting to set */
1789         ot->prop = RNA_def_enum(ot->srna, "type", prop_animchannel_settings_types, 0, "Type", "");
1790 }
1791
1792 static void ANIM_OT_channels_editable_toggle(wmOperatorType *ot)
1793 {
1794         PropertyRNA *prop;
1795         
1796         /* identifiers */
1797         ot->name = "Toggle Channel Editability";
1798         ot->idname = "ANIM_OT_channels_editable_toggle";
1799         ot->description = "Toggle editability of selected channels";
1800         
1801         /* api callbacks */
1802         ot->exec = animchannels_setflag_exec;
1803         ot->poll = animedit_poll_channels_active;
1804         
1805         /* flags */
1806         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1807         
1808         /* props */
1809         /* flag-setting mode */
1810         RNA_def_enum(ot->srna, "mode", prop_animchannel_setflag_types, ACHANNEL_SETFLAG_TOGGLE, "Mode", "");
1811         /* setting to set */
1812         prop = RNA_def_enum(ot->srna, "type", prop_animchannel_settings_types, ACHANNEL_SETTING_PROTECT, "Type", "");
1813         RNA_def_property_flag(prop, PROP_HIDDEN); /* internal hack - don't expose */
1814 }
1815
1816 /* ********************** Expand Channels Operator *********************** */
1817
1818 static int animchannels_expand_exec(bContext *C, wmOperator *op)
1819 {
1820         bAnimContext ac;
1821         bool onlysel = true;
1822         
1823         /* get editor data */
1824         if (ANIM_animdata_get_context(C, &ac) == 0)
1825                 return OPERATOR_CANCELLED;
1826                 
1827         /* only affect selected channels? */
1828         if (RNA_boolean_get(op->ptr, "all"))
1829                 onlysel = false;
1830         
1831         /* modify setting */
1832         setflag_anim_channels(&ac, ACHANNEL_SETTING_EXPAND, ACHANNEL_SETFLAG_ADD, onlysel, false);
1833         
1834         /* send notifier that things have changed */
1835         WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL);
1836         
1837         return OPERATOR_FINISHED;
1838 }
1839
1840 static void ANIM_OT_channels_expand(wmOperatorType *ot)
1841 {
1842         /* identifiers */
1843         ot->name = "Expand Channels";
1844         ot->idname = "ANIM_OT_channels_expand";
1845         ot->description = "Expand (i.e. open) all selected expandable animation channels";
1846         
1847         /* api callbacks */
1848         ot->exec = animchannels_expand_exec;
1849         ot->poll = animedit_poll_channels_active;
1850         
1851         /* flags */
1852         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1853         
1854         /* props */
1855         ot->prop = RNA_def_boolean(ot->srna, "all", 1, "All", "Expand all channels (not just selected ones)");
1856 }
1857
1858 /* ********************** Collapse Channels Operator *********************** */
1859
1860 static int animchannels_collapse_exec(bContext *C, wmOperator *op)
1861 {
1862         bAnimContext ac;
1863         bool onlysel = true;
1864         
1865         /* get editor data */
1866         if (ANIM_animdata_get_context(C, &ac) == 0)
1867                 return OPERATOR_CANCELLED;
1868                 
1869         /* only affect selected channels? */
1870         if (RNA_boolean_get(op->ptr, "all"))
1871                 onlysel = false;
1872         
1873         /* modify setting */
1874         setflag_anim_channels(&ac, ACHANNEL_SETTING_EXPAND, ACHANNEL_SETFLAG_CLEAR, onlysel, false);
1875         
1876         /* send notifier that things have changed */
1877         WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL);
1878         
1879         return OPERATOR_FINISHED;
1880 }
1881
1882 static void ANIM_OT_channels_collapse(wmOperatorType *ot)
1883 {
1884         /* identifiers */
1885         ot->name = "Collapse Channels";
1886         ot->idname = "ANIM_OT_channels_collapse";
1887         ot->description = "Collapse (i.e. close) all selected expandable animation channels";
1888         
1889         /* api callbacks */
1890         ot->exec = animchannels_collapse_exec;
1891         ot->poll = animedit_poll_channels_active;
1892         
1893         /* flags */
1894         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1895         
1896         /* props */
1897         ot->prop = RNA_def_boolean(ot->srna, "all", true, "All", "Collapse all channels (not just selected ones)");
1898 }
1899
1900 /* ************ Remove All "Empty" AnimData Blocks Operator ********* */
1901 /* We define "empty" AnimData blocks here as those which have all 3 of criteria:
1902  *  1) No active action OR that active actions are empty
1903  *     Assuming that all legitimate entries will have an action,
1904  *     and that empty actions
1905  *  2) No NLA Tracks + NLA Strips
1906  *     Assuming that users haven't set up any of these as "placeholders"
1907  *     for convenience sake, and that most that exist were either unintentional
1908  *     or are no longer wanted
1909  *  3) No drivers
1910  */
1911  
1912 static int animchannels_clean_empty_exec(bContext *C, wmOperator *UNUSED(op))
1913 {
1914         bAnimContext ac;
1915         
1916         ListBase anim_data = {NULL, NULL};
1917         bAnimListElem *ale;
1918         int filter;
1919         
1920         /* get editor data */
1921         if (ANIM_animdata_get_context(C, &ac) == 0)
1922                 return OPERATOR_CANCELLED;
1923         
1924         /* get animdata blocks */
1925         filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_ANIMDATA);
1926         ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
1927         
1928         for (ale = anim_data.first; ale; ale = ale->next) {
1929                 ID *id = ale->id;
1930                 AnimData *adt = ale->data;
1931                 
1932                 bool action_empty  = false;
1933                 bool nla_empty     = false;
1934                 bool drivers_empty = false;
1935                 
1936                 /* sanity checks */
1937                 BLI_assert((id != NULL) && (adt != NULL));
1938                 
1939                 /* check if this is "empty" and can be deleted */
1940                 /* (For now, there are only these 3 criteria) */
1941                 
1942                 /* 1) Active Action is missing or empty */
1943                 if (ELEM(NULL, adt->action, adt->action->curves.first)) {
1944                         action_empty = true;
1945                 }
1946                 else {
1947                         /* TODO: check for keyframe + fmodifier data on these too */
1948                 }
1949                 
1950                 /* 2) No NLA Tracks and/or NLA Strips */
1951                 if (adt->nla_tracks.first == NULL) {
1952                         nla_empty = true;
1953                 }
1954                 else {
1955                         NlaTrack *nlt;
1956                         
1957                         /* empty tracks? */
1958                         for (nlt = adt->nla_tracks.first; nlt; nlt = nlt->next) {
1959                                 if (nlt->strips.first) {
1960                                         /* stop searching, as we found one that actually had stuff we don't want lost 
1961                                          * NOTE: nla_empty gets reset to false, as a previous track may have been empty
1962                                          */
1963                                         nla_empty = false;
1964                                         break;
1965                                 }
1966                                 else if (nlt->strips.first == NULL) {
1967                                         /* this track is empty, but another one may still have stuff in it, so can't break yet */
1968                                         nla_empty = true;
1969                                 }
1970                         }
1971                 }
1972                 
1973                 /* 3) Drivers */
1974                 drivers_empty = (adt->drivers.first == NULL);
1975                 
1976                 
1977                 /* remove AnimData? */
1978                 if (action_empty && nla_empty && drivers_empty) {
1979                         BKE_free_animdata(id);
1980                 }
1981         }
1982         
1983         /* free temp data */
1984         ANIM_animdata_freelist(&anim_data);
1985         
1986         /* send notifier that things have changed */
1987         WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL);
1988         
1989         return OPERATOR_FINISHED;
1990 }
1991
1992 static void ANIM_OT_channels_clean_empty(wmOperatorType *ot)
1993 {
1994         /* identifiers */
1995         ot->name = "Remove Empty Animation Data";
1996         ot->idname = "ANIM_OT_channels_clean_empty";
1997         ot->description = "Delete all empty animation data containers from visible datablocks";
1998         
1999         /* api callbacks */
2000         ot->exec = animchannels_clean_empty_exec;
2001         ot->poll = animedit_poll_channels_nla_tweakmode_off;
2002         
2003         /* flags */
2004         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2005 }
2006
2007 /* ******************* Reenable Disabled Operator ******************* */
2008
2009 static int animchannels_enable_poll(bContext *C)
2010 {
2011         ScrArea *sa = CTX_wm_area(C);
2012         
2013         /* channels region test */
2014         /* TODO: could enhance with actually testing if channels region? */
2015         if (ELEM(NULL, sa, CTX_wm_region(C)))
2016                 return 0;
2017                 
2018         /* animation editor test - Action/Dopesheet/etc. and Graph only */
2019         if (ELEM(sa->spacetype, SPACE_ACTION, SPACE_IPO) == 0)
2020                 return 0;
2021                 
2022         return 1;
2023 }
2024
2025 static int animchannels_enable_exec(bContext *C, wmOperator *UNUSED(op))
2026 {
2027         bAnimContext ac;
2028         
2029         ListBase anim_data = {NULL, NULL};
2030         bAnimListElem *ale;
2031         int filter;
2032         
2033         /* get editor data */
2034         if (ANIM_animdata_get_context(C, &ac) == 0)
2035                 return OPERATOR_CANCELLED;
2036         
2037         /* filter data */
2038         filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_NODUPLIS);
2039         ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
2040         
2041         /* loop through filtered data and clean curves */
2042         for (ale = anim_data.first; ale; ale = ale->next) {
2043                 FCurve *fcu = (FCurve *)ale->data;
2044                 
2045                 /* remove disabled flags from F-Curves */
2046                 fcu->flag &= ~FCURVE_DISABLED;
2047                 
2048                 /* for drivers, let's do the same too */
2049                 if (fcu->driver)
2050                         fcu->driver->flag &= ~DRIVER_FLAG_INVALID;
2051                         
2052                 /* tag everything for updates - in particular, this is needed to get drivers working again */
2053                 ale->update |= ANIM_UPDATE_DEPS;
2054         }
2055         
2056         ANIM_animdata_update(&ac, &anim_data);
2057         ANIM_animdata_freelist(&anim_data);
2058                 
2059         /* send notifier that things have changed */
2060         WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL);
2061         
2062         return OPERATOR_FINISHED;
2063 }
2064
2065 static void ANIM_OT_channels_fcurves_enable(wmOperatorType *ot)
2066 {
2067         /* identifiers */
2068         ot->name = "Revive Disabled F-Curves";
2069         ot->idname = "ANIM_OT_channels_fcurves_enable";
2070         ot->description = "Clears 'disabled' tag from all F-Curves to get broken F-Curves working again";
2071         
2072         /* api callbacks */
2073         ot->exec = animchannels_enable_exec;
2074         ot->poll = animchannels_enable_poll;
2075         
2076         /* flags */
2077         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2078 }
2079
2080 /* ****************** Find / Set Filter Operator ******************** */
2081
2082 /* XXX: make this generic? */
2083 static int animchannels_find_poll(bContext *C)
2084 {
2085         ScrArea *sa = CTX_wm_area(C);
2086         
2087         if (sa == NULL)
2088                 return 0;
2089         
2090         /* animation editor with dopesheet */
2091         return ELEM(sa->spacetype, SPACE_ACTION, SPACE_IPO, SPACE_NLA);
2092 }
2093
2094 /* find_invoke() - Get initial channels */
2095 static int animchannels_find_invoke(bContext *C, wmOperator *op, const wmEvent *evt)
2096 {
2097         bAnimContext ac;
2098         
2099         /* get editor data */
2100         if (ANIM_animdata_get_context(C, &ac) == 0)
2101                 return OPERATOR_CANCELLED;
2102         
2103         /* set initial filter text, and enable filter */
2104         RNA_string_set(op->ptr, "query", ac.ads->searchstr);
2105         
2106         /* defer to popup */
2107         return WM_operator_props_popup(C, op, evt);
2108 }
2109
2110 /* find_exec() -  Called to set the value */
2111 static int animchannels_find_exec(bContext *C, wmOperator *op)
2112 {
2113         bAnimContext ac;
2114         
2115         /* get editor data */
2116         if (ANIM_animdata_get_context(C, &ac) == 0)
2117                 return OPERATOR_CANCELLED;
2118         
2119         /* update filter text, and ensure that filter is enabled if there's something there
2120          * NOTE: we turn the filter off if there's nothing (this is a quick shortcut for dismissing)
2121          */
2122         RNA_string_get(op->ptr, "query", ac.ads->searchstr);
2123         
2124         if (ac.ads->searchstr[0]) {
2125                 ac.ads->filterflag |= ADS_FILTER_BY_FCU_NAME;
2126         }
2127         else {
2128                 ac.ads->filterflag &= ~ADS_FILTER_BY_FCU_NAME;
2129         }
2130         
2131         /* redraw */
2132         WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL);
2133         
2134         return OPERATOR_FINISHED;
2135 }
2136
2137 static void ANIM_OT_channels_find(wmOperatorType *ot)
2138 {
2139         /* identifiers */
2140         ot->name = "Find Channels";
2141         ot->idname = "ANIM_OT_channels_find";
2142         ot->description = "Filter the set of channels shown to only include those with matching names";
2143         
2144         /* callbacks */
2145         ot->invoke = animchannels_find_invoke;
2146         ot->exec = animchannels_find_exec;
2147         ot->poll = animchannels_find_poll;
2148         
2149         /* flags */
2150         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2151         
2152         /* properties */
2153         ot->prop = RNA_def_string(ot->srna, "query", "Query", sizeof(((bDopeSheet *)NULL)->searchstr), "", "Text to search for in channel names");
2154 }
2155
2156 /* ********************** Select All Operator *********************** */
2157
2158 static int animchannels_deselectall_exec(bContext *C, wmOperator *op)
2159 {
2160         bAnimContext ac;
2161         
2162         /* get editor data */
2163         if (ANIM_animdata_get_context(C, &ac) == 0)
2164                 return OPERATOR_CANCELLED;
2165                 
2166         /* 'standard' behavior - check if selected, then apply relevant selection */
2167         if (RNA_boolean_get(op->ptr, "invert"))
2168                 ANIM_deselect_anim_channels(&ac, ac.data, ac.datatype, false, ACHANNEL_SETFLAG_INVERT);
2169         else
2170                 ANIM_deselect_anim_channels(&ac, ac.data, ac.datatype, true, ACHANNEL_SETFLAG_ADD);
2171         
2172         /* send notifier that things have changed */
2173         WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_SELECTED, NULL);
2174         
2175         return OPERATOR_FINISHED;
2176 }
2177  
2178 static void ANIM_OT_channels_select_all_toggle(wmOperatorType *ot)
2179 {
2180         /* identifiers */
2181         ot->name = "Select All";
2182         ot->idname = "ANIM_OT_channels_select_all_toggle";
2183         ot->description = "Toggle selection of all animation channels";
2184         
2185         /* api callbacks */
2186         ot->exec = animchannels_deselectall_exec;
2187         ot->poll = animedit_poll_channels_nla_tweakmode_off;
2188         
2189         /* flags */
2190         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2191         
2192         /* props */
2193         ot->prop = RNA_def_boolean(ot->srna, "invert", false, "Invert", "");
2194 }
2195
2196 /* ******************** Borderselect Operator *********************** */
2197
2198 static void borderselect_anim_channels(bAnimContext *ac, rcti *rect, short selectmode)
2199 {
2200         ListBase anim_data = {NULL, NULL};
2201         bAnimListElem *ale;
2202         int filter;
2203         
2204         SpaceNla *snla = (SpaceNla *)ac->sl;
2205         View2D *v2d = &ac->ar->v2d;
2206         rctf rectf;
2207         float ymin, ymax;
2208         
2209         /* set initial y extents */
2210         if (ac->datatype == ANIMCONT_NLA) {
2211                 ymin = (float)(-NLACHANNEL_HEIGHT(snla));
2212                 ymax = 0.0f;
2213         }
2214         else {
2215                 ymin = 0.0f;
2216                 ymax = (float)(-ACHANNEL_HEIGHT);
2217         }
2218         
2219         /* convert border-region to view coordinates */
2220         UI_view2d_region_to_view(v2d, rect->xmin, rect->ymin + 2, &rectf.xmin, &rectf.ymin);
2221         UI_view2d_region_to_view(v2d, rect->xmax, rect->ymax - 2, &rectf.xmax, &rectf.ymax);
2222         
2223         /* filter data */
2224         filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_LIST_CHANNELS);
2225         ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
2226         
2227         /* loop over data, doing border select */
2228         for (ale = anim_data.first; ale; ale = ale->next) {
2229                 if (ac->datatype == ANIMCONT_NLA)
2230                         ymin = ymax - NLACHANNEL_STEP(snla);
2231                 else
2232                         ymin = ymax - ACHANNEL_STEP;
2233                 
2234                 /* if channel is within border-select region, alter it */
2235                 if (!((ymax < rectf.ymin) || (ymin > rectf.ymax))) {
2236                         /* set selection flags only */
2237                         ANIM_channel_setting_set(ac, ale, ACHANNEL_SETTING_SELECT, selectmode);
2238                         
2239                         /* type specific actions */
2240                         switch (ale->type) {
2241                                 case ANIMTYPE_GROUP:
2242                                 {
2243                                         bActionGroup *agrp = (bActionGroup *)ale->data;
2244                                         
2245                                         /* Armatures-Specific Feature:
2246                                          * See mouse_anim_channels() -> ANIMTYPE_GROUP case for more details (T38737)
2247                                          */
2248                                         if ((ac->ads->filterflag & ADS_FILTER_ONLYSEL) == 0) {
2249                                                 if ((ale->id) && (GS(ale->id->name) == ID_OB)) {
2250                                                         Object *ob = (Object *)ale->id;
2251                                                         
2252                                                         if (ob->type == OB_ARMATURE) {
2253                                                                 /* Assume for now that any group with corresponding name is what we want
2254                                                                  * (i.e. for an armature whose location is animated, things would break
2255                                                                  * if the user were to add a bone named "Location").
2256                                                                  *
2257                                                                  * TODO: check the first F-Curve or so to be sure...
2258                                                                  */
2259                                                                 bPoseChannel *pchan = BKE_pose_channel_find_name(ob->pose, agrp->name);
2260                                                                 
2261                                                                 if (agrp->flag & AGRP_SELECTED) {
2262                                                                         ED_pose_bone_select(ob, pchan, true);
2263                                                                 }
2264                                                                 else {
2265                                                                         ED_pose_bone_select(ob, pchan, false);
2266                                                                 }
2267                                                         }
2268                                                 }
2269                                         }
2270                                         
2271                                         /* always clear active flag after doing this */
2272                                         agrp->flag &= ~AGRP_ACTIVE;
2273                                         break;
2274                                 }
2275                                 case ANIMTYPE_NLATRACK:
2276                                 {
2277                                         NlaTrack *nlt = (NlaTrack *)ale->data;
2278                                         
2279                                         /* for now, it's easier just to do this here manually, as defining a new type 
2280                                          * currently adds complications when doing other stuff 
2281                                          */
2282                                         ACHANNEL_SET_FLAG(nlt, selectmode, NLATRACK_SELECTED);
2283                                         break;
2284                                 }
2285                         }
2286                 }
2287                 
2288                 /* set minimum extent to be the maximum of the next channel */
2289                 ymax = ymin;
2290         }
2291         
2292         /* cleanup */
2293         ANIM_animdata_freelist(&anim_data);
2294 }
2295
2296 /* ------------------- */
2297
2298 static int animchannels_borderselect_exec(bContext *C, wmOperator *op)
2299 {
2300         bAnimContext ac;
2301         rcti rect;
2302         short selectmode = 0;
2303         int gesture_mode;
2304         bool extend;
2305         
2306         /* get editor data */
2307         if (ANIM_animdata_get_context(C, &ac) == 0)
2308                 return OPERATOR_CANCELLED;
2309         
2310         /* get settings from operator */
2311         WM_operator_properties_border_to_rcti(op, &rect);
2312         
2313         gesture_mode = RNA_int_get(op->ptr, "gesture_mode");
2314         extend = RNA_boolean_get(op->ptr, "extend");
2315
2316         if (!extend)
2317                 ANIM_deselect_anim_channels(&ac, ac.data, ac.datatype, true, ACHANNEL_SETFLAG_CLEAR);
2318
2319         if (gesture_mode == GESTURE_MODAL_SELECT)
2320                 selectmode = ACHANNEL_SETFLAG_ADD;
2321         else
2322                 selectmode = ACHANNEL_SETFLAG_CLEAR;
2323         
2324         /* apply borderselect animation channels */
2325         borderselect_anim_channels(&ac, &rect, selectmode);
2326         
2327         /* send notifier that things have changed */
2328         WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_SELECTED, NULL);
2329         
2330         return OPERATOR_FINISHED;
2331
2332
2333 static void ANIM_OT_channels_select_border(wmOperatorType *ot)
2334 {
2335         /* identifiers */
2336         ot->name = "Border Select";
2337         ot->idname = "ANIM_OT_channels_select_border";
2338         ot->description = "Select all animation channels within the specified region";
2339         
2340         /* api callbacks */
2341         ot->invoke = WM_border_select_invoke;
2342         ot->exec = animchannels_borderselect_exec;
2343         ot->modal = WM_border_select_modal;
2344         ot->cancel = WM_border_select_cancel;
2345         
2346         ot->poll = animedit_poll_channels_nla_tweakmode_off;
2347         
2348         /* flags */
2349         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2350         
2351         /* rna */
2352         WM_operator_properties_gesture_border(ot, true);
2353 }
2354
2355 /* ******************* Rename Operator ***************************** */
2356 /* Allow renaming some channels by clicking on them */
2357
2358 static void rename_anim_channels(bAnimContext *ac, int channel_index)
2359 {
2360         ListBase anim_data = {NULL, NULL};
2361         bAnimChannelType *acf;
2362         bAnimListElem *ale;
2363         int filter;
2364         
2365         /* get the channel that was clicked on */
2366         /* filter channels */
2367         filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_LIST_CHANNELS);
2368         ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
2369         
2370         /* get channel from index */
2371         ale = BLI_findlink(&anim_data, channel_index);
2372         if (ale == NULL) {
2373                 /* channel not found */
2374                 if (G.debug & G_DEBUG)
2375                         printf("Error: animation channel (index = %d) not found in rename_anim_channels()\n", channel_index);
2376                 
2377                 ANIM_animdata_freelist(&anim_data);
2378                 return;
2379         }
2380         
2381         /* check that channel can be renamed */
2382         acf = ANIM_channel_get_typeinfo(ale);
2383         if (acf && acf->name_prop) {
2384                 PointerRNA ptr;
2385                 PropertyRNA *prop;
2386                 
2387                 /* ok if we can get name property to edit from this channel */
2388                 if (acf->name_prop(ale, &ptr, &prop)) {
2389                         /* actually showing the rename textfield is done on redraw,
2390                          * so here we just store the index of this channel in the 
2391                          * dopesheet data, which will get utilized when drawing the
2392                          * channel...
2393                          *
2394                          * +1 factor is for backwards compat issues
2395                          */
2396                         if (ac->ads) {
2397                                 ac->ads->renameIndex = channel_index + 1;
2398                         }
2399                 }
2400         }
2401         
2402         /* free temp data and tag for refresh */
2403         ANIM_animdata_freelist(&anim_data);
2404         ED_region_tag_redraw(ac->ar);
2405 }
2406
2407 static int animchannels_rename_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *event)
2408 {
2409         bAnimContext ac;
2410         ARegion *ar;
2411         View2D *v2d;
2412         int channel_index;
2413         float x, y;
2414         
2415         /* get editor data */
2416         if (ANIM_animdata_get_context(C, &ac) == 0)
2417                 return OPERATOR_CANCELLED;
2418                 
2419         /* get useful pointers from animation context data */
2420         ar = ac.ar;
2421         v2d = &ar->v2d;
2422         
2423         /* figure out which channel user clicked in 
2424          * Note: although channels technically start at (y = ACHANNEL_FIRST), we need to adjust by half a channel's height
2425          *              so that the tops of channels get caught ok. Since ACHANNEL_FIRST is really ACHANNEL_HEIGHT, we simply use
2426          *              ACHANNEL_HEIGHT_HALF.
2427          */
2428         UI_view2d_region_to_view(v2d, event->mval[0], event->mval[1], &x, &y);
2429         
2430         if (ac.datatype == ANIMCONT_NLA) {
2431                 SpaceNla *snla = (SpaceNla *)ac.sl;
2432                 UI_view2d_listview_view_to_cell(v2d, NLACHANNEL_NAMEWIDTH, NLACHANNEL_STEP(snla), 0, (float)NLACHANNEL_HEIGHT_HALF(snla), x, y, NULL, &channel_index);
2433         }
2434         else {
2435                 UI_view2d_listview_view_to_cell(v2d, ACHANNEL_NAMEWIDTH, ACHANNEL_STEP, 0, (float)ACHANNEL_HEIGHT_HALF, x, y, NULL, &channel_index);
2436         }
2437         
2438         /* handle click */
2439         rename_anim_channels(&ac, channel_index);
2440         
2441         return OPERATOR_FINISHED;
2442 }
2443
2444 static void ANIM_OT_channels_rename(wmOperatorType *ot)
2445 {
2446         /* identifiers */
2447         ot->name = "Rename Channels";
2448         ot->idname = "ANIM_OT_channels_rename";
2449         ot->description = "Rename animation channel under mouse";
2450         
2451         /* api callbacks */
2452         ot->invoke = animchannels_rename_invoke;
2453         ot->poll = animedit_poll_channels_active;
2454 }
2455
2456 /* ******************** Mouse-Click Operator *********************** */
2457 /* Handle selection changes due to clicking on channels. Settings will get caught by UI code... */
2458
2459 static int mouse_anim_channels(bContext *C, bAnimContext *ac, int channel_index, short selectmode)
2460 {
2461         ListBase anim_data = {NULL, NULL};
2462         bAnimListElem *ale;
2463         int filter;
2464         int notifierFlags = 0;
2465         
2466         /* get the channel that was clicked on */
2467         /* filter channels */
2468         filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_LIST_CHANNELS);
2469         ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
2470         
2471         /* get channel from index */
2472         ale = BLI_findlink(&anim_data, channel_index);
2473         if (ale == NULL) {
2474                 /* channel not found */
2475                 if (G.debug & G_DEBUG)
2476                         printf("Error: animation channel (index = %d) not found in mouse_anim_channels()\n", channel_index);
2477                 
2478                 ANIM_animdata_freelist(&anim_data);
2479                 return 0;
2480         }
2481
2482         /* selectmode -1 is a special case for ActionGroups only, which selects all of the channels underneath it only... */
2483         /* TODO: should this feature be extended to work with other channel types too? */
2484         if ((selectmode == -1) && (ale->type != ANIMTYPE_GROUP)) {
2485                 /* normal channels should not behave normally in this case */
2486                 ANIM_animdata_freelist(&anim_data);
2487                 return 0;
2488         }
2489
2490         /* action to take depends on what channel we've got */
2491         /* WARNING: must keep this in sync with the equivalent function in nla_channels.c */
2492         switch (ale->type) {
2493                 case ANIMTYPE_SCENE:
2494                 {
2495                         Scene *sce = (Scene *)ale->data;
2496                         AnimData *adt = sce->adt;
2497                         
2498                         /* set selection status */
2499                         if (selectmode == SELECT_INVERT) {
2500                                 /* swap select */
2501                                 sce->flag ^= SCE_DS_SELECTED;
2502                                 if (adt) adt->flag ^= ADT_UI_SELECTED;
2503                         }
2504                         else {
2505                                 sce->flag |= SCE_DS_SELECTED;
2506                                 if (adt) adt->flag |= ADT_UI_SELECTED;
2507                         }
2508                         
2509                         notifierFlags |= (ND_ANIMCHAN | NA_SELECTED);
2510                         break;
2511                 }
2512                 case ANIMTYPE_OBJECT:
2513                 {
2514                         bDopeSheet *ads = (bDopeSheet *)ac->data;
2515                         Scene *sce = (Scene *)ads->source;
2516                         Base *base = (Base *)ale->data;
2517                         Object *ob = base->object;
2518                         AnimData *adt = ob->adt;
2519                         
2520                         /* set selection status */
2521                         if (selectmode == SELECT_INVERT) {
2522                                 /* swap select */
2523                                 base->flag ^= SELECT;
2524                                 ob->flag = base->flag;
2525                                 
2526                                 if (adt) adt->flag ^= ADT_UI_SELECTED;
2527                         }
2528                         else {
2529                                 Base *b;
2530                                 
2531                                 /* deselect all */
2532                                 /* TODO: should this deselect all other types of channels too? */
2533                                 for (b = sce->base.first; b; b = b->next) {
2534                                         b->flag &= ~SELECT;
2535                                         b->object->flag = b->flag;
2536                                         if (b->object->adt) b->object->adt->flag &= ~(ADT_UI_SELECTED | ADT_UI_ACTIVE);
2537                                 }
2538                                 
2539                                 /* select object now */
2540                                 base->flag |= SELECT;
2541                                 ob->flag |= SELECT;
2542                                 if (adt) adt->flag |= ADT_UI_SELECTED;
2543                         }
2544                         
2545                         /* change active object - regardless of whether it is now selected [T37883] */
2546                         ED_base_object_activate(C, base); /* adds notifier */
2547                         
2548                         if ((adt) && (adt->flag & ADT_UI_SELECTED))
2549                                 adt->flag |= ADT_UI_ACTIVE;
2550                         
2551                         notifierFlags |= (ND_ANIMCHAN | NA_SELECTED);
2552                         break;
2553                 }
2554                 case ANIMTYPE_FILLACTD: /* Action Expander */
2555                 case ANIMTYPE_DSMAT:    /* Datablock AnimData Expanders */
2556                 case ANIMTYPE_DSLAM:
2557                 case ANIMTYPE_DSCAM:
2558                 case ANIMTYPE_DSCUR:
2559                 case ANIMTYPE_DSSKEY:
2560                 case ANIMTYPE_DSWOR:
2561                 case ANIMTYPE_DSPART:
2562                 case ANIMTYPE_DSMBALL:
2563                 case ANIMTYPE_DSARM:
2564                 case ANIMTYPE_DSMESH:
2565                 case ANIMTYPE_DSNTREE:
2566                 case ANIMTYPE_DSTEX:
2567                 case ANIMTYPE_DSLAT:
2568                 case ANIMTYPE_DSLINESTYLE:
2569                 case ANIMTYPE_DSSPK:
2570                 {
2571                         /* sanity checking... */
2572                         if (ale->adt) {
2573                                 /* select/deselect */
2574                                 if (selectmode == SELECT_INVERT) {
2575                                         /* inverse selection status of this AnimData block only */
2576                                         ale->adt->flag ^= ADT_UI_SELECTED;
2577                                 }
2578                                 else {
2579                                         /* select AnimData block by itself */
2580                                         ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, false, ACHANNEL_SETFLAG_CLEAR);
2581                                         ale->adt->flag |= ADT_UI_SELECTED;
2582                                 }
2583                                 
2584                                 /* set active? */
2585                                 if ((ale->adt) && (ale->adt->flag & ADT_UI_SELECTED))
2586                                         ale->adt->flag |= ADT_UI_ACTIVE;
2587                         }
2588                         
2589                         notifierFlags |= (ND_ANIMCHAN | NA_SELECTED);
2590                         break;
2591                 }
2592                 case ANIMTYPE_GROUP: 
2593                 {
2594                         bActionGroup *agrp = (bActionGroup *)ale->data;
2595                         
2596                         Object *ob = NULL;
2597                         bPoseChannel *pchan = NULL;
2598                         
2599                         
2600                         /* Armatures-Specific Feature:
2601                          * Since groups are used to collect F-Curves of the same Bone by default
2602                          * (via Keying Sets) so that they can be managed better, we try to make
2603                          * things here easier for animators by mapping group selection to bone
2604                          * selection. 
2605                          *
2606                          * Only do this if "Only Selected" dopesheet filter is not active, or else it
2607                          * becomes too unpredictable/tricky to manage
2608                          */
2609                         if ((ac->ads->filterflag & ADS_FILTER_ONLYSEL) == 0) {
2610                                 if ((ale->id) && (GS(ale->id->name) == ID_OB)) {
2611                                         ob = (Object *)ale->id;
2612                                         
2613                                         if (ob->type == OB_ARMATURE) {
2614                                                 /* Assume for now that any group with corresponding name is what we want
2615                                                  * (i.e. for an armature whose location is animated, things would break
2616                                                  * if the user were to add a bone named "Location").
2617                                                  *
2618                                                  * TODO: check the first F-Curve or so to be sure...
2619                                                  */
2620                                                 pchan = BKE_pose_channel_find_name(ob->pose, agrp->name);
2621                                         }       
2622                                 }
2623                         }
2624                         
2625                         /* select/deselect group */
2626                         if (selectmode == SELECT_INVERT) {
2627                                 /* inverse selection status of this group only */
2628                                 agrp->flag ^= AGRP_SELECTED;
2629                         }
2630                         else if (selectmode == -1) {
2631                                 /* select all in group (and deselect everthing else) */
2632                                 FCurve *fcu;
2633                                 
2634                                 /* deselect all other channels */
2635                                 ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, false, ACHANNEL_SETFLAG_CLEAR);
2636                                 if (pchan) ED_pose_de_selectall(ob, SEL_DESELECT, false);
2637                                 
2638                                 /* only select channels in group and group itself */
2639                                 for (fcu = agrp->channels.first; fcu && fcu->grp == agrp; fcu = fcu->next)
2640                                         fcu->flag |= FCURVE_SELECTED;
2641                                 agrp->flag |= AGRP_SELECTED;
2642                         }
2643                         else {
2644                                 /* select group by itself */
2645                                 ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, false, ACHANNEL_SETFLAG_CLEAR);
2646                                 if (pchan) ED_pose_de_selectall(ob, SEL_DESELECT, false);
2647                                 
2648                                 agrp->flag |= AGRP_SELECTED;
2649                         }
2650                         
2651                         /* if group is selected now, make group the 'active' one in the visible list */
2652                         if (agrp->flag & AGRP_SELECTED) {
2653                                 ANIM_set_active_channel(ac, ac->data, ac->datatype, filter, agrp, ANIMTYPE_GROUP);
2654                                 if (pchan) ED_pose_bone_select(ob, pchan, true);
2655                         }
2656                         else {
2657                                 ANIM_set_active_channel(ac, ac->data, ac->datatype, filter, NULL, ANIMTYPE_GROUP);
2658                                 if (pchan) ED_pose_bone_select(ob, pchan, false);
2659                         }
2660                                 
2661                         notifierFlags |= (ND_ANIMCHAN | NA_SELECTED);
2662                         break;
2663                 }
2664                 case ANIMTYPE_FCURVE: 
2665                 {
2666                         FCurve *fcu = (FCurve *)ale->data;
2667                         
2668                         /* select/deselect */
2669                         if (selectmode == SELECT_INVERT) {
2670                                 /* inverse selection status of this F-Curve only */
2671                                 fcu->flag ^= FCURVE_SELECTED;
2672                         }
2673                         else {
2674                                 /* select F-Curve by itself */
2675                                 ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, false, ACHANNEL_SETFLAG_CLEAR);
2676                                 fcu->flag |= FCURVE_SELECTED;
2677                         }
2678                         
2679                         /* if F-Curve is selected now, make F-Curve the 'active' one in the visible list */
2680                         if (fcu->flag & FCURVE_SELECTED)
2681                                 ANIM_set_active_channel(ac, ac->data, ac->datatype, filter, fcu, ANIMTYPE_FCURVE);
2682                                 
2683                         notifierFlags |= (ND_ANIMCHAN | NA_SELECTED);
2684                         break;
2685                 }
2686                 case ANIMTYPE_SHAPEKEY: 
2687                 {
2688                         KeyBlock *kb = (KeyBlock *)ale->data;
2689                         
2690                         /* select/deselect */
2691                         if (selectmode == SELECT_INVERT) {
2692                                 /* inverse selection status of this ShapeKey only */
2693                                 kb->flag ^= KEYBLOCK_SEL;
2694                         }
2695                         else {
2696                                 /* select ShapeKey by itself */
2697                                 ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, false, ACHANNEL_SETFLAG_CLEAR);
2698                                 kb->flag |= KEYBLOCK_SEL;
2699                         }
2700                                 
2701                         notifierFlags |= (ND_ANIMCHAN | NA_SELECTED);
2702                         break;
2703                 }
2704                 case ANIMTYPE_GPDATABLOCK:
2705                 {
2706                         bGPdata *gpd = (bGPdata *)ale->data;
2707                         
2708                         /* toggle expand 
2709                          *      - although the triangle widget already allows this, the whole channel can also be used for this purpose
2710                          */
2711                         gpd->flag ^= GP_DATA_EXPAND;
2712                         
2713                         notifierFlags |= (ND_ANIMCHAN | NA_EDITED);
2714                         break;
2715                 }
2716                 case ANIMTYPE_GPLAYER:
2717                 {
2718                         bGPDlayer *gpl = (bGPDlayer *)ale->data;
2719                         
2720                         /* select/deselect */
2721                         if (selectmode == SELECT_INVERT) {
2722                                 /* invert selection status of this layer only */
2723                                 gpl->flag ^= GP_LAYER_SELECT;
2724                         }
2725                         else {
2726                                 /* select layer by itself */
2727                                 ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, false, ACHANNEL_SETFLAG_CLEAR);
2728                                 gpl->flag |= GP_LAYER_SELECT;
2729                         }
2730                         
2731                         notifierFlags |= (ND_ANIMCHAN | NA_EDITED);
2732                         break;
2733                 }
2734                 case ANIMTYPE_MASKDATABLOCK:
2735                 {
2736                         Mask *mask = (Mask *)ale->data;
2737                         
2738                         /* toggle expand
2739                          *      - although the triangle widget already allows this, the whole channel can also be used for this purpose
2740                          */
2741                         mask->flag ^= MASK_ANIMF_EXPAND;
2742                         
2743                         notifierFlags |= (ND_ANIMCHAN | NA_EDITED);
2744                         break;
2745                 }
2746                 case ANIMTYPE_MASKLAYER:
2747                 {
2748                         MaskLayer *masklay = (MaskLayer *)ale->data;
2749                         
2750                         /* select/deselect */
2751                         if (selectmode == SELECT_INVERT) {
2752                                 /* invert selection status of this layer only */
2753                                 masklay->flag ^= MASK_LAYERFLAG_SELECT;
2754                         }
2755                         else {
2756                                 /* select layer by itself */
2757                                 ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, false, ACHANNEL_SETFLAG_CLEAR);
2758                                 masklay->flag |= MASK_LAYERFLAG_SELECT;
2759                         }
2760                         
2761                         notifierFlags |= (ND_ANIMCHAN | NA_EDITED);
2762                         break;
2763                 }
2764                 default:
2765                         if (G.debug & G_DEBUG)
2766                                 printf("Error: Invalid channel type in mouse_anim_channels()\n");
2767                         break;
2768         }
2769         
2770         /* free channels */
2771         ANIM_animdata_freelist(&anim_data);
2772         
2773         /* return notifier flags */
2774         return notifierFlags;
2775 }
2776
2777 /* ------------------- */
2778
2779 /* handle clicking */
2780 static int animchannels_mouseclick_invoke(bContext *C, wmOperator *op, const wmEvent *event)
2781 {
2782         bAnimContext ac;
2783         ARegion *ar;
2784         View2D *v2d;
2785         int channel_index;
2786         int notifierFlags = 0;
2787         short selectmode;
2788         float x, y;
2789         
2790         
2791         /* get editor data */
2792         if (ANIM_animdata_get_context(C, &ac) == 0)
2793                 return OPERATOR_CANCELLED;
2794                 
2795         /* get useful pointers from animation context data */
2796         ar = ac.ar;
2797         v2d = &ar->v2d;
2798         
2799         /* select mode is either replace (deselect all, then add) or add/extend */
2800         if (RNA_boolean_get(op->ptr, "extend"))
2801                 selectmode = SELECT_INVERT;
2802         else if (RNA_boolean_get(op->ptr, "children_only"))
2803                 selectmode = -1;  /* this is a bit of a special case for ActionGroups only... should it be removed or extended to all instead? */
2804         else
2805                 selectmode = SELECT_REPLACE;
2806         
2807         /* figure out which channel user clicked in 
2808          * Note: although channels technically start at (y = ACHANNEL_FIRST), we need to adjust by half a channel's height
2809          *              so that the tops of channels get caught ok. Since ACHANNEL_FIRST is really ACHANNEL_HEIGHT, we simply use
2810          *              ACHANNEL_HEIGHT_HALF.
2811          */
2812         UI_view2d_region_to_view(v2d, event->mval[0], event->mval[1], &x, &y);
2813         UI_view2d_listview_view_to_cell(v2d, ACHANNEL_NAMEWIDTH, ACHANNEL_STEP, 0, (float)ACHANNEL_HEIGHT_HALF, x, y, NULL, &channel_index);
2814         
2815         /* handle mouse-click in the relevant channel then */
2816         notifierFlags = mouse_anim_channels(C, &ac, channel_index, selectmode);
2817         
2818         /* set notifier that things have changed */
2819         WM_event_add_notifier(C, NC_ANIMATION | notifierFlags, NULL);
2820         
2821         return OPERATOR_FINISHED;
2822 }
2823  
2824 static void ANIM_OT_channels_click(wmOperatorType *ot)
2825 {
2826         PropertyRNA *prop;
2827         
2828         /* identifiers */
2829         ot->name = "Mouse Click on Channels";
2830         ot->idname = "ANIM_OT_channels_click";
2831         ot->description = "Handle mouse-clicks over animation channels";
2832         
2833         /* api callbacks */
2834         ot->invoke = animchannels_mouseclick_invoke;
2835         ot->poll = animedit_poll_channels_active;
2836         
2837         /* flags */
2838         ot->flag = OPTYPE_UNDO;
2839         
2840         /* properties */
2841         /* NOTE: don't save settings, otherwise, can end up with some weird behaviour (sticky extend) */
2842         prop = RNA_def_boolean(ot->srna, "extend", false, "Extend Select", ""); // SHIFTKEY
2843         RNA_def_property_flag(prop, PROP_SKIP_SAVE);
2844         
2845         prop = RNA_def_boolean(ot->srna, "children_only", false, "Select Children Only", ""); // CTRLKEY|SHIFTKEY
2846         RNA_def_property_flag(prop, PROP_SKIP_SAVE);
2847 }
2848
2849 /* ************************************************************************** */
2850 /* Operator Registration */
2851
2852 void ED_operatortypes_animchannels(void)
2853 {
2854         WM_operatortype_append(ANIM_OT_channels_select_all_toggle);
2855         WM_operatortype_append(ANIM_OT_channels_select_border);
2856         
2857         WM_operatortype_append(ANIM_OT_channels_click);
2858         WM_operatortype_append(ANIM_OT_channels_rename);
2859         
2860         WM_operatortype_append(ANIM_OT_channels_find);
2861         
2862         WM_operatortype_append(ANIM_OT_channels_setting_enable);
2863         WM_operatortype_append(ANIM_OT_channels_setting_disable);
2864         WM_operatortype_append(ANIM_OT_channels_setting_toggle);
2865
2866         WM_operatortype_append(ANIM_OT_channels_delete);
2867
2868         /* XXX does this need to be a separate operator? */
2869         WM_operatortype_append(ANIM_OT_channels_editable_toggle);
2870         
2871         WM_operatortype_append(ANIM_OT_channels_move);
2872         
2873         WM_operatortype_append(ANIM_OT_channels_expand);
2874         WM_operatortype_append(ANIM_OT_channels_collapse);
2875         
2876         WM_operatortype_append(ANIM_OT_channels_fcurves_enable);
2877         
2878         WM_operatortype_append(ANIM_OT_channels_clean_empty);
2879         
2880         WM_operatortype_append(ANIM_OT_channels_group);
2881         WM_operatortype_append(ANIM_OT_channels_ungroup);
2882 }
2883
2884 // TODO: check on a poll callback for this, to get hotkeys into menus
2885 void ED_keymap_animchannels(wmKeyConfig *keyconf)
2886 {
2887         wmKeyMap *keymap = WM_keymap_find(keyconf, "Animation Channels", 0, 0);
2888         wmKeyMapItem *kmi;
2889         
2890         /* click-select */
2891         /* XXX for now, only leftmouse.... */
2892         WM_keymap_add_item(keymap, "ANIM_OT_channels_click", LEFTMOUSE, KM_PRESS, 0, 0);
2893         RNA_boolean_set(WM_keymap_add_item(keymap, "ANIM_OT_channels_click", LEFTMOUSE, KM_PRESS, KM_SHIFT, 0)->ptr, "extend", true);
2894         RNA_boolean_set(WM_keymap_add_item(keymap, "ANIM_OT_channels_click", LEFTMOUSE, KM_PRESS, KM_CTRL | KM_SHIFT, 0)->ptr, "children_only", true);
2895
2896         /* rename */
2897         WM_keymap_add_item(keymap, "ANIM_OT_channels_rename", LEFTMOUSE, KM_PRESS, KM_CTRL, 0);
2898         WM_keymap_add_item(keymap, "ANIM_OT_channels_rename", LEFTMOUSE, KM_DBL_CLICK, 0, 0);
2899         
2900         /* find (i.e. a shortcut for setting the name filter) */
2901         WM_keymap_add_item(keymap, "ANIM_OT_channels_find", FKEY, KM_PRESS, KM_CTRL, 0);
2902         
2903         /* deselect all */
2904         WM_keymap_add_item(keymap, "ANIM_OT_channels_select_all_toggle", AKEY, KM_PRESS, 0, 0);
2905         RNA_boolean_set(WM_keymap_add_item(keymap, "ANIM_OT_channels_select_all_toggle&q