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