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