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