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