fix [#33580] Masking keyframes disappear from dope sheet when using undo.
[blender.git] / source / blender / editors / animation / anim_channels_edit.c
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * The Original Code is Copyright (C) 2009 Blender Foundation, Joshua Leung
19  * All rights reserved.
20  *
21  * Contributor(s): Joshua Leung
22  *
23  * ***** END GPL LICENSE BLOCK *****
24  */
25
26 /** \file blender/editors/animation/anim_channels_edit.c
27  *  \ingroup edanimation
28  */
29
30
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h> 
34
35 #include "MEM_guardedalloc.h"
36
37 #include "BLI_blenlib.h"
38 #include "BLI_utildefines.h"
39 #include "BKE_library.h"
40
41 #include "DNA_anim_types.h"
42 #include "DNA_object_types.h"
43 #include "DNA_scene_types.h"
44 #include "DNA_key_types.h"
45 #include "DNA_gpencil_types.h"
46 #include "DNA_mask_types.h"
47
48 #include "RNA_access.h"
49 #include "RNA_define.h"
50
51 #include "BKE_action.h"
52 #include "BKE_fcurve.h"
53 #include "BKE_gpencil.h"
54 #include "BKE_context.h"
55 #include "BKE_mask.h"
56 #include "BKE_global.h"
57
58 #include "UI_view2d.h"
59
60 #include "ED_anim_api.h"
61 #include "ED_keyframes_edit.h" // XXX move the select modes out of there!
62 #include "ED_screen.h"
63
64 #include "WM_api.h"
65 #include "WM_types.h"
66
67 /* ************************************************************************** */
68 /* CHANNELS API - Exposed API */
69
70 /* -------------------------- Selection ------------------------------------- */
71
72 /* Set the given animation-channel as the active one for the active context */
73 // TODO: extend for animdata types...
74 void ANIM_set_active_channel(bAnimContext *ac, void *data, short datatype, int filter, void *channel_data, short channel_type)
75 {
76         ListBase anim_data = {NULL, NULL};
77         bAnimListElem *ale;
78         
79         /* try to build list of filtered items */
80         ANIM_animdata_filter(ac, &anim_data, filter, data, datatype);
81         if (anim_data.first == NULL)
82                 return;
83                 
84         /* only clear the 'active' flag for the channels of the same type */
85         for (ale = anim_data.first; ale; ale = ale->next) {
86                 /* skip if types don't match */
87                 if (channel_type != ale->type)
88                         continue;
89                 
90                 /* flag to set depends on type */
91                 switch (ale->type) {
92                         case ANIMTYPE_GROUP:
93                         {
94                                 bActionGroup *agrp = (bActionGroup *)ale->data;
95                                 
96                                 ACHANNEL_SET_FLAG(agrp, ACHANNEL_SETFLAG_CLEAR, AGRP_ACTIVE);
97                         }
98                         break;
99                         case ANIMTYPE_FCURVE:
100                         {
101                                 FCurve *fcu = (FCurve *)ale->data;
102                                 
103                                 ACHANNEL_SET_FLAG(fcu, ACHANNEL_SETFLAG_CLEAR, FCURVE_ACTIVE);
104                         }
105                         break;
106                         case ANIMTYPE_NLATRACK:
107                         {
108                                 NlaTrack *nlt = (NlaTrack *)ale->data;
109                                 
110                                 ACHANNEL_SET_FLAG(nlt, ACHANNEL_SETFLAG_CLEAR, NLATRACK_ACTIVE);
111                         }
112                         break;
113                         
114                         case ANIMTYPE_FILLACTD: /* Action Expander */
115                         case ANIMTYPE_DSMAT:    /* Datablock AnimData Expanders */
116                         case ANIMTYPE_DSLAM:
117                         case ANIMTYPE_DSCAM:
118                         case ANIMTYPE_DSCUR:
119                         case ANIMTYPE_DSSKEY:
120                         case ANIMTYPE_DSWOR:
121                         case ANIMTYPE_DSPART:
122                         case ANIMTYPE_DSMBALL:
123                         case ANIMTYPE_DSARM:
124                         case ANIMTYPE_DSMESH:
125                         case ANIMTYPE_DSTEX:
126                         case ANIMTYPE_DSLAT:
127                         case ANIMTYPE_DSSPK:
128                         {
129                                 /* need to verify that this data is valid for now */
130                                 if (ale->adt) {
131                                         ACHANNEL_SET_FLAG(ale->adt, ACHANNEL_SETFLAG_CLEAR, ADT_UI_ACTIVE);
132                                 }
133                         }
134                         break;
135                 }
136         }
137         
138         /* set active flag */
139         if (channel_data) {
140                 switch (channel_type) {
141                         case ANIMTYPE_GROUP:
142                         {
143                                 bActionGroup *agrp = (bActionGroup *)channel_data;
144                                 agrp->flag |= AGRP_ACTIVE;
145                         }
146                         break;
147                         case ANIMTYPE_FCURVE:
148                         {
149                                 FCurve *fcu = (FCurve *)channel_data;
150                                 fcu->flag |= FCURVE_ACTIVE;
151                         }
152                         break;
153                         case ANIMTYPE_NLATRACK:
154                         {
155                                 NlaTrack *nlt = (NlaTrack *)channel_data;
156                                 nlt->flag |= NLATRACK_ACTIVE;
157                         }
158                         break;
159                                 
160                         case ANIMTYPE_FILLACTD: /* Action Expander */
161                         case ANIMTYPE_DSMAT:    /* Datablock AnimData Expanders */
162                         case ANIMTYPE_DSLAM:
163                         case ANIMTYPE_DSCAM:
164                         case ANIMTYPE_DSCUR:
165                         case ANIMTYPE_DSSKEY:
166                         case ANIMTYPE_DSWOR:
167                         case ANIMTYPE_DSPART:
168                         case ANIMTYPE_DSMBALL:
169                         case ANIMTYPE_DSARM:
170                         case ANIMTYPE_DSMESH:
171                         case ANIMTYPE_DSLAT:
172                         case ANIMTYPE_DSSPK:
173                         {
174                                 /* need to verify that this data is valid for now */
175                                 if (ale && ale->adt) {
176                                         ale->adt->flag |= ADT_UI_ACTIVE;
177                                 }
178                         }
179                         break;
180                 }
181         }
182         
183         /* clean up */
184         BLI_freelistN(&anim_data);
185 }
186
187 /* Deselect all animation channels 
188  *      - data: pointer to datatype, as contained in bAnimContext
189  *      - datatype: the type of data that 'data' represents (eAnimCont_Types)
190  *      - test: check if deselecting instead of selecting
191  *      - sel: eAnimChannels_SetFlag;
192  */
193 void ANIM_deselect_anim_channels(bAnimContext *ac, void *data, short datatype, short test, short sel)
194 {
195         ListBase anim_data = {NULL, NULL};
196         bAnimListElem *ale;
197         int filter;
198         
199         /* filter data */
200         /* NOTE: no list visible, otherwise, we get dangling */
201         filter = ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_CHANNELS;
202         ANIM_animdata_filter(ac, &anim_data, filter, data, datatype);
203         
204         /* See if we should be selecting or deselecting */
205         if (test) {
206                 for (ale = anim_data.first; ale; ale = ale->next) {
207                         if (sel == 0) 
208                                 break;
209                         
210                         switch (ale->type) {
211                                 case ANIMTYPE_SCENE:
212                                         if (ale->flag & SCE_DS_SELECTED)
213                                                 sel = ACHANNEL_SETFLAG_CLEAR;
214                                         break;
215                                 case ANIMTYPE_OBJECT:
216                                 #if 0   /* for now, do not take object selection into account, since it gets too annoying */
217                                         if (ale->flag & SELECT)
218                                                 sel = ACHANNEL_SETFLAG_CLEAR;
219                                 #endif
220                                         break;
221                                 case ANIMTYPE_GROUP:
222                                         if (ale->flag & AGRP_SELECTED)
223                                                 sel = ACHANNEL_SETFLAG_CLEAR;
224                                         break;
225                                 case ANIMTYPE_FCURVE:
226                                         if (ale->flag & FCURVE_SELECTED)
227                                                 sel = ACHANNEL_SETFLAG_CLEAR;
228                                         break;
229                                 case ANIMTYPE_SHAPEKEY:
230                                         if (ale->flag & KEYBLOCK_SEL)
231                                                 sel = ACHANNEL_SETFLAG_CLEAR;
232                                         break;
233                                 case ANIMTYPE_NLATRACK:
234                                         if (ale->flag & NLATRACK_SELECTED)
235                                                 sel = ACHANNEL_SETFLAG_CLEAR;
236                                         break;
237                                         
238                                 case ANIMTYPE_FILLACTD: /* Action Expander */
239                                 case ANIMTYPE_DSMAT:    /* Datablock AnimData Expanders */
240                                 case ANIMTYPE_DSLAM:
241                                 case ANIMTYPE_DSCAM:
242                                 case ANIMTYPE_DSCUR:
243                                 case ANIMTYPE_DSSKEY:
244                                 case ANIMTYPE_DSWOR:
245                                 case ANIMTYPE_DSPART:
246                                 case ANIMTYPE_DSMBALL:
247                                 case ANIMTYPE_DSARM:
248                                 case ANIMTYPE_DSMESH:
249                                 case ANIMTYPE_DSNTREE:
250                                 case ANIMTYPE_DSTEX:
251                                 case ANIMTYPE_DSLAT:
252                                 case ANIMTYPE_DSSPK:
253                                 {
254                                         if ((ale->adt) && (ale->adt->flag & ADT_UI_SELECTED))
255                                                 sel = ACHANNEL_SETFLAG_CLEAR;
256                                 }
257                                 break;
258                                         
259                                 case ANIMTYPE_GPLAYER:
260                                         if (ale->flag & GP_LAYER_SELECT)
261                                                 sel = ACHANNEL_SETFLAG_CLEAR;
262                                         break;
263                                 case ANIMTYPE_MASKLAYER:
264                                         if (ale->flag & MASK_LAYERFLAG_SELECT)
265                                                 sel = ACHANNEL_SETFLAG_CLEAR;
266                                         break;
267                         }
268                 }
269         }
270                 
271         /* Now set the flags */
272         for (ale = anim_data.first; ale; ale = ale->next) {
273                 switch (ale->type) {
274                         case ANIMTYPE_SCENE:
275                         {
276                                 Scene *scene = (Scene *)ale->data;
277                                 
278                                 ACHANNEL_SET_FLAG(scene, sel, SCE_DS_SELECTED);
279                                 
280                                 if (scene->adt) {
281                                         ACHANNEL_SET_FLAG(scene, sel, ADT_UI_SELECTED);
282                                 }
283                         }
284                         break;
285                         case ANIMTYPE_OBJECT:
286                         #if 0   /* for now, do not take object selection into account, since it gets too annoying */
287                                 {
288                                         Base *base = (Base *)ale->data;
289                                         Object *ob = base->object;
290                                         
291                                         ACHANNEL_SET_FLAG(base, sel, SELECT);
292                                         ACHANNEL_SET_FLAG(ob, sel, SELECT);
293                                         
294                                         if (ob->adt) {
295                                                 ACHANNEL_SET_FLAG(ob, sel, ADT_UI_SELECTED);
296                                         }
297                                 }
298                         #endif
299                                 break;
300                         case ANIMTYPE_GROUP:
301                         {
302                                 bActionGroup *agrp = (bActionGroup *)ale->data;
303                                 
304                                 ACHANNEL_SET_FLAG(agrp, sel, AGRP_SELECTED);
305                                 agrp->flag &= ~AGRP_ACTIVE;
306                         }
307                         break;
308                         case ANIMTYPE_FCURVE:
309                         {
310                                 FCurve *fcu = (FCurve *)ale->data;
311                                 
312                                 ACHANNEL_SET_FLAG(fcu, sel, FCURVE_SELECTED);
313                                 fcu->flag &= ~FCURVE_ACTIVE;
314                         }
315                         break;
316                         case ANIMTYPE_SHAPEKEY:
317                         {
318                                 KeyBlock *kb = (KeyBlock *)ale->data;
319                                 
320                                 ACHANNEL_SET_FLAG(kb, sel, KEYBLOCK_SEL);
321                         }
322                         break;
323                         case ANIMTYPE_NLATRACK:
324                         {
325                                 NlaTrack *nlt = (NlaTrack *)ale->data;
326                                 
327                                 ACHANNEL_SET_FLAG(nlt, sel, NLATRACK_SELECTED);
328                                 nlt->flag &= ~NLATRACK_ACTIVE;
329                         }
330                         break;
331                                 
332                         case ANIMTYPE_FILLACTD: /* Action Expander */
333                         case ANIMTYPE_DSMAT:    /* Datablock AnimData Expanders */
334                         case ANIMTYPE_DSLAM:
335                         case ANIMTYPE_DSCAM:
336                         case ANIMTYPE_DSCUR:
337                         case ANIMTYPE_DSSKEY:
338                         case ANIMTYPE_DSWOR:
339                         case ANIMTYPE_DSPART:
340                         case ANIMTYPE_DSMBALL:
341                         case ANIMTYPE_DSARM:
342                         case ANIMTYPE_DSMESH:
343                         case ANIMTYPE_DSNTREE:
344                         case ANIMTYPE_DSTEX:
345                         case ANIMTYPE_DSLAT:
346                         case ANIMTYPE_DSSPK:
347                         {
348                                 /* need to verify that this data is valid for now */
349                                 if (ale->adt) {
350                                         ACHANNEL_SET_FLAG(ale->adt, sel, ADT_UI_SELECTED);
351                                         ale->adt->flag &= ~ADT_UI_ACTIVE;
352                                 }
353                         }
354                         break;
355                                 
356                         case ANIMTYPE_GPLAYER:
357                         {
358                                 bGPDlayer *gpl = (bGPDlayer *)ale->data;
359                                 
360                                 ACHANNEL_SET_FLAG(gpl, sel, GP_LAYER_SELECT);
361                         }
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                         }
370                         break;
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 (agrp->channels.first == NULL) {
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 ((act->curves.first == NULL) && (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 short 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 short 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 short 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 short 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 short 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 short (*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         short is_sel = 0, is_untouchable = 0;
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                 }
801                 break;
802                 case ANIMTYPE_FCURVE:
803                 {
804                         FCurve *fcu = (FCurve *)channel;
805                         
806                         is_sel = SEL_FCU(fcu);
807                 }
808                 break;
809                 case ANIMTYPE_NLATRACK:
810                 {
811                         NlaTrack *nlt = (NlaTrack *)channel;
812                         
813                         is_sel = SEL_NLT(nlt);
814                 }
815                 break;
816                         
817                 default:
818                         printf("rearrange_animchannel_add_to_islands(): don't know how to handle channels of type %d\n", type);
819                         return;
820         }
821         
822         /* do we need to add to a new island? */
823         if ((island == NULL) ||                                 /* 1) no islands yet */
824             ((island->flag & REORDER_ISLAND_SELECTED) == 0) ||  /* 2) unselected islands have single channels only - to allow up/down movement */
825             (is_sel == 0))                                      /* 3) if channel is unselected, stop existing island (it was either wrong sel status, or full already) */
826         {
827                 /* create a new island now */
828                 island = MEM_callocN(sizeof(tReorderChannelIsland), "tReorderChannelIsland");
829                 BLI_addtail(islands, island);
830                 
831                 if (is_sel)
832                         island->flag |= REORDER_ISLAND_SELECTED;
833                 if (is_untouchable)
834                         island->flag |= REORDER_ISLAND_UNTOUCHABLE;
835         }
836
837         /* add channel to island - need to remove it from its existing list first though */
838         BLI_remlink(srcList, channel);
839         BLI_addtail(&island->channels, channel);
840 }
841
842 /* flatten islands out into a single list again */
843 static void rearrange_animchannel_flatten_islands(ListBase *islands, ListBase *srcList)
844 {
845         tReorderChannelIsland *island, *isn = NULL;
846         
847         /* make sure srcList is empty now */
848         BLI_assert(srcList->first == NULL);
849         
850         /* go through merging islands */
851         for (island = islands->first; island; island = isn) {
852                 isn = island->next;
853                 
854                 /* merge island channels back to main list, then delete the island */
855                 BLI_movelisttolist(srcList, &island->channels);
856                 BLI_freelinkN(islands, island);
857         }
858 }
859
860 /* ............................. */
861
862 /* performing rearranging of channels using islands */
863 static short rearrange_animchannel_islands(ListBase *list, AnimChanRearrangeFp rearrange_func, short mode, short type)
864 {
865         ListBase islands = {NULL, NULL};
866         Link *channel, *chanNext = NULL;
867         short done = FALSE;
868         
869         /* don't waste effort on an empty list */
870         if (list->first == NULL)
871                 return 0;
872         
873         /* group channels into islands */
874         for (channel = list->first; channel; channel = chanNext) {
875                 chanNext = channel->next;
876                 rearrange_animchannel_add_to_islands(&islands, list, channel, type);
877         }
878         
879         /* perform moving of selected islands now, but only if there is more than one of 'em so that something will happen 
880          *      - scanning of the list is performed in the opposite direction to the direction we're moving things, so that we 
881          *        shouldn't need to encounter items we've moved already
882          */
883         if (islands.first != islands.last) {
884                 tReorderChannelIsland *first = (mode > 0) ? islands.last : islands.first;
885                 tReorderChannelIsland *island, *isn = NULL;
886                 
887                 for (island = first; island; island = isn) {
888                         isn = (mode > 0) ? island->prev : island->next;
889                         
890                         /* perform rearranging */
891                         if (rearrange_func(&islands, island)) {
892                                 island->flag |= REORDER_ISLAND_MOVED;
893                                 done = TRUE;
894                         }
895                 }
896         }
897         
898         /* ungroup islands */
899         rearrange_animchannel_flatten_islands(&islands, list);
900         
901         /* did we do anything? */
902         return done;
903 }
904
905 /* NLA Specific Stuff ----------------------------------------------------- */
906
907 /* Change the order NLA Tracks within NLA Stack
908  * ! NLA tracks are displayed in opposite order, so directions need care
909  *      mode: REARRANGE_ANIMCHAN_*  
910  */
911 static void rearrange_nla_channels(bAnimContext *UNUSED(ac), AnimData *adt, short mode)
912 {
913         AnimChanRearrangeFp rearrange_func;
914         
915         /* hack: invert mode so that functions will work in right order */
916         mode *= -1;
917         
918         /* get rearranging function */
919         rearrange_func = rearrange_get_mode_func(mode);
920         if (rearrange_func == NULL)
921                 return;
922         
923         /* only consider NLA data if it's accessible */
924         //if (EXPANDED_DRVD(adt) == 0)
925         //      return;
926         
927         /* perform rearranging on tracks list */
928         rearrange_animchannel_islands(&adt->nla_tracks, rearrange_func, mode, ANIMTYPE_NLATRACK);
929 }
930
931 /* Drivers Specific Stuff ------------------------------------------------- */
932
933 /* Change the order drivers within AnimData block
934  *      mode: REARRANGE_ANIMCHAN_*  
935  */
936 static void rearrange_driver_channels(bAnimContext *UNUSED(ac), AnimData *adt, short mode)
937 {
938         /* get rearranging function */
939         AnimChanRearrangeFp rearrange_func = rearrange_get_mode_func(mode);
940         
941         if (rearrange_func == NULL)
942                 return;
943         
944         /* only consider drivers if they're accessible */
945         if (EXPANDED_DRVD(adt) == 0)
946                 return;
947         
948         /* perform rearranging on drivers list (drivers are really just F-Curves) */
949         rearrange_animchannel_islands(&adt->drivers, rearrange_func, mode, ANIMTYPE_FCURVE);
950 }
951
952 /* Action Specific Stuff ------------------------------------------------- */
953
954 /* make sure all action-channels belong to a group (and clear action's list) */
955 static void split_groups_action_temp(bAction *act, bActionGroup *tgrp)
956 {
957         bActionGroup *agrp;
958         FCurve *fcu;
959         
960         if (act == NULL)
961                 return;
962         
963         /* Separate F-Curves into lists per group */
964         for (agrp = act->groups.first; agrp; agrp = agrp->next) {
965                 if (agrp->channels.first) {
966                         fcu = agrp->channels.last;
967                         act->curves.first = fcu->next;
968                         
969                         fcu = agrp->channels.first;
970                         fcu->prev = NULL;
971                         
972                         fcu = agrp->channels.last;
973                         fcu->next = NULL;
974                 }
975         }
976         
977         /* Initialize memory for temp-group */
978         memset(tgrp, 0, sizeof(bActionGroup));
979         tgrp->flag |= (AGRP_EXPANDED | AGRP_TEMP);
980         BLI_strncpy(tgrp->name, "#TempGroup", sizeof(tgrp->name));
981         
982         /* Move any action-channels not already moved, to the temp group */
983         if (act->curves.first) {
984                 /* start of list */
985                 fcu = act->curves.first;
986                 fcu->prev = NULL;
987                 tgrp->channels.first = fcu;
988                 act->curves.first = NULL;
989                 
990                 /* end of list */
991                 fcu = act->curves.last;
992                 fcu->next = NULL;
993                 tgrp->channels.last = fcu;
994                 act->curves.last = NULL;
995         }
996         
997         /* Add temp-group to list */
998         BLI_addtail(&act->groups, tgrp);
999 }
1000
1001 /* link lists of channels that groups have */
1002 static void join_groups_action_temp(bAction *act)
1003 {
1004         bActionGroup *agrp;
1005         
1006         for (agrp = act->groups.first; agrp; agrp = agrp->next) {
1007                 ListBase tempGroup;
1008                 
1009                 /* add list of channels to action's channels */
1010                 tempGroup = agrp->channels;
1011                 BLI_movelisttolist(&act->curves, &agrp->channels);
1012                 agrp->channels = tempGroup;
1013                 
1014                 /* clear moved flag */
1015                 agrp->flag &= ~AGRP_MOVED;
1016                 
1017                 /* if temp-group... remove from list (but don't free as it's on the stack!) */
1018                 if (agrp->flag & AGRP_TEMP) {
1019                         BLI_remlink(&act->groups, agrp);
1020                         break;
1021                 }
1022         }
1023 }
1024
1025 /* Change the order of anim-channels within action 
1026  *      mode: REARRANGE_ANIMCHAN_*  
1027  */
1028 static void rearrange_action_channels(bAnimContext *ac, bAction *act, short mode)
1029 {
1030         bActionGroup tgrp;
1031         short do_channels;
1032         
1033         /* get rearranging function */
1034         AnimChanRearrangeFp rearrange_func = rearrange_get_mode_func(mode);
1035         
1036         if (rearrange_func == NULL)
1037                 return;
1038         
1039         /* make sure we're only operating with groups (vs a mixture of groups+curves) */
1040         split_groups_action_temp(act, &tgrp);
1041         
1042         /* rearrange groups first 
1043          *      - the group's channels will only get considered if nothing happened when rearranging the groups
1044          *        i.e. the rearrange function returned 0
1045          */
1046         do_channels = rearrange_animchannel_islands(&act->groups, rearrange_func, mode, ANIMTYPE_GROUP) == 0;
1047         
1048         if (do_channels) {
1049                 bActionGroup *agrp;
1050                 
1051                 for (agrp = act->groups.first; agrp; agrp = agrp->next) {
1052                         /* only consider F-Curves if they're visible (group expanded) */
1053                         if (EXPANDED_AGRP(ac, agrp)) {
1054                                 rearrange_animchannel_islands(&agrp->channels, rearrange_func, mode, ANIMTYPE_FCURVE);
1055                         }
1056                 }
1057         }
1058         
1059         /* assemble lists into one list (and clear moved tags) */
1060         join_groups_action_temp(act);
1061 }
1062
1063 /* ------------------- */
1064
1065 static int animchannels_rearrange_exec(bContext *C, wmOperator *op)
1066 {
1067         bAnimContext ac;
1068         short mode;
1069         
1070         /* get editor data */
1071         if (ANIM_animdata_get_context(C, &ac) == 0)
1072                 return OPERATOR_CANCELLED;
1073                 
1074         /* get mode */
1075         mode = RNA_enum_get(op->ptr, "direction");
1076         
1077         /* method to move channels depends on the editor */
1078         if (ac.datatype == ANIMCONT_GPENCIL) {
1079                 /* Grease Pencil channels */
1080                 printf("Grease Pencil not supported for moving yet\n");
1081         }
1082         else if (ac.datatype == ANIMCONT_MASK) {
1083                 /* Grease Pencil channels */
1084                 printf("Mask does not supported for moving yet\n");
1085         }
1086         else if (ac.datatype == ANIMCONT_ACTION) {
1087                 /* Directly rearrange action's channels */
1088                 rearrange_action_channels(&ac, ac.data, mode);
1089         }
1090         else {
1091                 ListBase anim_data = {NULL, NULL};
1092                 bAnimListElem *ale;
1093                 int filter;
1094                 
1095                 /* get animdata blocks */
1096                 filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_ANIMDATA);
1097                 ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
1098                 
1099                 for (ale = anim_data.first; ale; ale = ale->next) {
1100                         AnimData *adt = ale->data;
1101                         
1102                         switch (ac.datatype) {
1103                                 case ANIMCONT_NLA: /* NLA-tracks only */
1104                                         rearrange_nla_channels(&ac, adt, mode);
1105                                         break;
1106                                 
1107                                 case ANIMCONT_DRIVERS: /* Drivers list only */
1108                                         rearrange_driver_channels(&ac, adt, mode);
1109                                         break;
1110                                 
1111                                 case ANIMCONT_SHAPEKEY: // DOUBLE CHECK ME...
1112                                         
1113                                 default: /* some collection of actions */
1114                                         if (adt->action)
1115                                                 rearrange_action_channels(&ac, adt->action, mode);
1116                                         else if (G.debug & G_DEBUG)
1117                                                 printf("Animdata has no action\n");
1118                                         break;
1119                         }
1120                 }
1121                 
1122                 /* free temp data */
1123                 BLI_freelistN(&anim_data);
1124         }
1125         
1126         /* send notifier that things have changed */
1127         WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL);
1128         
1129         return OPERATOR_FINISHED;
1130 }
1131
1132 static void ANIM_OT_channels_move(wmOperatorType *ot)
1133 {
1134         /* identifiers */
1135         ot->name = "Move Channels";
1136         ot->idname = "ANIM_OT_channels_move";
1137         ot->description = "Rearrange selected animation channels";
1138         
1139         /* api callbacks */
1140         ot->exec = animchannels_rearrange_exec;
1141         ot->poll = animedit_poll_channels_nla_tweakmode_off;
1142         
1143         /* flags */
1144         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1145         
1146         /* props */
1147         ot->prop = RNA_def_enum(ot->srna, "direction", prop_animchannel_rearrange_types, REARRANGE_ANIMCHAN_DOWN, "Direction", "");
1148 }
1149
1150 /* ******************** Delete Channel Operator *********************** */
1151
1152 static int animchannels_delete_exec(bContext *C, wmOperator *UNUSED(op))
1153 {
1154         bAnimContext ac;
1155         ListBase anim_data = {NULL, NULL};
1156         bAnimListElem *ale;
1157         int filter;
1158         
1159         /* get editor data */
1160         if (ANIM_animdata_get_context(C, &ac) == 0)
1161                 return OPERATOR_CANCELLED;
1162         
1163         /* cannot delete in shapekey */
1164         if (ac.datatype == ANIMCONT_SHAPEKEY) 
1165                 return OPERATOR_CANCELLED;
1166                 
1167                 
1168         /* do groups only first (unless in Drivers mode, where there are none) */
1169         if (ac.datatype != ANIMCONT_DRIVERS) {
1170                 /* filter data */
1171                 filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_SEL | ANIMFILTER_LIST_CHANNELS | ANIMFILTER_FOREDIT | ANIMFILTER_NODUPLIS);
1172                 ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
1173                 
1174                 /* delete selected groups and their associated channels */
1175                 for (ale = anim_data.first; ale; ale = ale->next) {
1176                         /* only groups - don't check other types yet, since they may no-longer exist */
1177                         if (ale->type == ANIMTYPE_GROUP) {
1178                                 bActionGroup *agrp = (bActionGroup *)ale->data;
1179                                 AnimData *adt = ale->adt;
1180                                 FCurve *fcu, *fcn;
1181                                 
1182                                 /* skip this group if no AnimData available, as we can't safely remove the F-Curves */
1183                                 if (adt == NULL)
1184                                         continue;
1185                                 
1186                                 /* delete all of the Group's F-Curves, but no others */
1187                                 for (fcu = agrp->channels.first; fcu && fcu->grp == agrp; fcu = fcn) {
1188                                         fcn = fcu->next;
1189                                         
1190                                         /* remove from group and action, then free */
1191                                         action_groups_remove_channel(adt->action, fcu);
1192                                         free_fcurve(fcu);
1193                                 }
1194                                 
1195                                 /* free the group itself */
1196                                 if (adt->action)
1197                                         BLI_freelinkN(&adt->action->groups, agrp);
1198                                 else
1199                                         MEM_freeN(agrp);
1200                         }
1201                 }
1202                 
1203                 /* cleanup */
1204                 BLI_freelistN(&anim_data);
1205         }
1206         
1207         /* filter data */
1208         filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_SEL | ANIMFILTER_FOREDIT | ANIMFILTER_NODUPLIS);
1209         ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
1210         
1211         /* delete selected data channels */
1212         for (ale = anim_data.first; ale; ale = ale->next) {
1213                 switch (ale->type) {
1214                         case ANIMTYPE_FCURVE: 
1215                         {
1216                                 /* F-Curves if we can identify its parent */
1217                                 AnimData *adt = ale->adt;
1218                                 FCurve *fcu = (FCurve *)ale->data;
1219                                 
1220                                 /* try to free F-Curve */
1221                                 ANIM_fcurve_delete_from_animdata(&ac, adt, fcu);
1222                         }
1223                         break;
1224                                 
1225                         case ANIMTYPE_GPLAYER:
1226                         {
1227                                 /* Grease Pencil layer */
1228                                 bGPdata *gpd = (bGPdata *)ale->id;
1229                                 bGPDlayer *gpl = (bGPDlayer *)ale->data;
1230                                 
1231                                 /* try to delete the layer's data and the layer itself */
1232                                 free_gpencil_frames(gpl);
1233                                 BLI_freelinkN(&gpd->layers, gpl);
1234                         }
1235                         break;
1236
1237                         case ANIMTYPE_MASKLAYER:
1238                         {
1239                                 /* Mask layer */
1240                                 Mask *mask = (Mask *)ale->id;
1241                                 MaskLayer *masklay = (MaskLayer *)ale->data;
1242
1243                                 /* try to delete the layer's data and the layer itself */
1244                                 BKE_mask_layer_remove(mask, masklay);
1245                         }
1246                         break;
1247                 }
1248         }
1249         
1250         /* cleanup */
1251         BLI_freelistN(&anim_data);
1252         
1253         /* send notifier that things have changed */
1254         WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL);
1255         
1256         return OPERATOR_FINISHED;
1257 }
1258  
1259 static void ANIM_OT_channels_delete(wmOperatorType *ot)
1260 {
1261         /* identifiers */
1262         ot->name = "Delete Channels";
1263         ot->idname = "ANIM_OT_channels_delete";
1264         ot->description = "Delete all selected animation channels";
1265         
1266         /* api callbacks */
1267         ot->exec = animchannels_delete_exec;
1268         ot->poll = animedit_poll_channels_active;
1269         
1270         /* flags */
1271         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1272 }
1273
1274 /* ******************** Set Channel Visibility Operator *********************** */
1275 /* NOTE: this operator is only valid in the Graph Editor channels region */
1276
1277 static int animchannels_visibility_set_exec(bContext *C, wmOperator *UNUSED(op))
1278 {
1279         bAnimContext ac;
1280         ListBase anim_data = {NULL, NULL};
1281         ListBase all_data = {NULL, NULL};
1282         bAnimListElem *ale;
1283         int filter;
1284         
1285         /* get editor data */
1286         if (ANIM_animdata_get_context(C, &ac) == 0)
1287                 return OPERATOR_CANCELLED;
1288         
1289         /* get list of all channels that selection may need to be flushed to 
1290          * - hierarchy mustn't affect what we have access to here...
1291          */
1292         filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_CHANNELS | ANIMFILTER_NODUPLIS);
1293         ANIM_animdata_filter(&ac, &all_data, filter, ac.data, ac.datatype);
1294                 
1295         /* hide all channels not selected
1296          * - hierarchy matters if we're doing this from the channels region
1297          *   since we only want to apply this to channels we can "see", 
1298          *   and have these affect their relatives
1299          * - but for Graph Editor, this gets used also from main region
1300          *   where hierarchy doesn't apply, as for [#21276]
1301          */
1302         if ((ac.spacetype == SPACE_IPO) && (ac.regiontype != RGN_TYPE_CHANNELS)) {
1303                 /* graph editor (case 2) */
1304                 filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_UNSEL | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_NODUPLIS);
1305         }
1306         else {
1307                 /* standard case */
1308                 filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_UNSEL | ANIMFILTER_NODUPLIS);
1309         }
1310         ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
1311         
1312         for (ale = anim_data.first; ale; ale = ale->next) {
1313                 /* clear setting first */
1314                 ANIM_channel_setting_set(&ac, ale, ACHANNEL_SETTING_VISIBLE, ACHANNEL_SETFLAG_CLEAR);
1315                 
1316                 /* now also flush selection status as appropriate 
1317                  * NOTE: in some cases, this may result in repeat flushing being performed
1318                  */
1319                 ANIM_flush_setting_anim_channels(&ac, &all_data, ale, ACHANNEL_SETTING_VISIBLE, 0);
1320         }
1321         
1322         BLI_freelistN(&anim_data);
1323         
1324         /* make all the selected channels visible */
1325         filter = (ANIMFILTER_SEL | ANIMFILTER_NODUPLIS);
1326         ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
1327
1328         for (ale = anim_data.first; ale; ale = ale->next) {
1329                 /* hack: skip object channels for now, since flushing those will always flush everything, but they are always included */
1330                 /* TODO: find out why this is the case, and fix that */
1331                 if (ale->type == ANIMTYPE_OBJECT)
1332                         continue;
1333
1334                 /* enable the setting */
1335                 ANIM_channel_setting_set(&ac, ale, ACHANNEL_SETTING_VISIBLE, ACHANNEL_SETFLAG_ADD);
1336
1337                 /* now, also flush selection status up/down as appropriate */
1338                 ANIM_flush_setting_anim_channels(&ac, &all_data, ale, ACHANNEL_SETTING_VISIBLE, 1);
1339         }
1340         
1341         BLI_freelistN(&anim_data);
1342         BLI_freelistN(&all_data);
1343         
1344         
1345         /* send notifier that things have changed */
1346         WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL);
1347         
1348         return OPERATOR_FINISHED;
1349 }
1350
1351 static void ANIM_OT_channels_visibility_set(wmOperatorType *ot)
1352 {
1353         /* identifiers */
1354         ot->name = "Set Visibility";
1355         ot->idname = "ANIM_OT_channels_visibility_set";
1356         ot->description = "Make only the selected animation channels visible in the Graph Editor";
1357         
1358         /* api callbacks */
1359         ot->exec = animchannels_visibility_set_exec;
1360         ot->poll = ED_operator_graphedit_active;
1361         
1362         /* flags */
1363         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1364 }
1365
1366
1367 /* ******************** Toggle Channel Visibility Operator *********************** */
1368 /* NOTE: this operator is only valid in the Graph Editor channels region */
1369
1370 static int animchannels_visibility_toggle_exec(bContext *C, wmOperator *UNUSED(op))
1371 {
1372         bAnimContext ac;
1373         ListBase anim_data = {NULL, NULL};
1374         ListBase all_data = {NULL, NULL};
1375         bAnimListElem *ale;
1376         int filter;
1377         short vis = ACHANNEL_SETFLAG_ADD;
1378         
1379         /* get editor data */
1380         if (ANIM_animdata_get_context(C, &ac) == 0)
1381                 return OPERATOR_CANCELLED;
1382                 
1383         /* get list of all channels that selection may need to be flushed to 
1384          * - hierarchy mustn't affect what we have access to here...
1385          */
1386         filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_CHANNELS | ANIMFILTER_NODUPLIS);
1387         ANIM_animdata_filter(&ac, &all_data, filter, ac.data, ac.datatype);
1388                 
1389         /* filter data
1390          * - restrict this to only applying on settings we can get to in the list
1391          */
1392         filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_SEL | ANIMFILTER_NODUPLIS);
1393         ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
1394         
1395         /* See if we should be making showing all selected or hiding */
1396         for (ale = anim_data.first; ale; ale = ale->next) {
1397                 /* set the setting in the appropriate way (if available) */
1398                 if (ANIM_channel_setting_get(&ac, ale, ACHANNEL_SETTING_VISIBLE)) {
1399                         vis = ACHANNEL_SETFLAG_CLEAR;
1400                         break;
1401                 }
1402         }
1403
1404         /* Now set the flags */
1405         for (ale = anim_data.first; ale; ale = ale->next) {
1406                 /* hack: skip object channels for now, since flushing those will always flush everything, but they are always included */
1407                 /* TODO: find out why this is the case, and fix that */
1408                 if (ale->type == ANIMTYPE_OBJECT)
1409                         continue;
1410
1411                 /* change the setting */
1412                 ANIM_channel_setting_set(&ac, ale, ACHANNEL_SETTING_VISIBLE, vis);
1413
1414                 /* now, also flush selection status up/down as appropriate */
1415                 ANIM_flush_setting_anim_channels(&ac, &all_data, ale, ACHANNEL_SETTING_VISIBLE, (vis == ACHANNEL_SETFLAG_ADD));
1416         }
1417         
1418         /* cleanup */
1419         BLI_freelistN(&anim_data);
1420         BLI_freelistN(&all_data);
1421         
1422         /* send notifier that things have changed */
1423         WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL);
1424         
1425         return OPERATOR_FINISHED;
1426 }
1427
1428 static void ANIM_OT_channels_visibility_toggle(wmOperatorType *ot)
1429 {
1430         /* identifiers */
1431         ot->name = "Toggle Visibility";
1432         ot->idname = "ANIM_OT_channels_visibility_toggle";
1433         ot->description = "Toggle visibility in Graph Editor of all selected animation channels";
1434         
1435         /* api callbacks */
1436         ot->exec = animchannels_visibility_toggle_exec;
1437         ot->poll = ED_operator_graphedit_active;
1438         
1439         /* flags */
1440         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1441 }
1442
1443 /* ********************** Set Flags Operator *********************** */
1444
1445 /* defines for setting animation-channel flags */
1446 static EnumPropertyItem prop_animchannel_setflag_types[] = {
1447         {ACHANNEL_SETFLAG_TOGGLE, "TOGGLE", 0, "Toggle", ""},
1448         {ACHANNEL_SETFLAG_CLEAR, "DISABLE", 0, "Disable", ""},
1449         {ACHANNEL_SETFLAG_ADD, "ENABLE", 0, "Enable", ""},
1450         {ACHANNEL_SETFLAG_INVERT, "INVERT", 0, "Invert", ""},
1451         {0, NULL, 0, NULL, NULL}
1452 };
1453
1454 /* defines for set animation-channel settings */
1455 // TODO: could add some more types, but those are really quite dependent on the mode...
1456 static EnumPropertyItem prop_animchannel_settings_types[] = {
1457         {ACHANNEL_SETTING_PROTECT, "PROTECT", 0, "Protect", ""},
1458         {ACHANNEL_SETTING_MUTE, "MUTE", 0, "Mute", ""},
1459         {0, NULL, 0, NULL, NULL}
1460 };
1461
1462
1463 /* ------------------- */
1464
1465 /* Set/clear a particular flag (setting) for all selected + visible channels 
1466  *      setting: the setting to modify
1467  *      mode: eAnimChannels_SetFlag
1468  *      onlysel: only selected channels get the flag set
1469  */
1470 // TODO: enable a setting which turns flushing on/off?
1471 static void setflag_anim_channels(bAnimContext *ac, short setting, short mode, short onlysel, short flush)
1472 {
1473         ListBase anim_data = {NULL, NULL};
1474         ListBase all_data = {NULL, NULL};
1475         bAnimListElem *ale;
1476         int filter;
1477         
1478         /* filter data that we need if flush is on */
1479         if (flush) {
1480                 /* get list of all channels that selection may need to be flushed to 
1481                  * - hierarchy visibility needs to be ignored so that settings can get flushed
1482                  *   "down" inside closed containers
1483                  */
1484                 filter = ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_CHANNELS;
1485                 ANIM_animdata_filter(ac, &all_data, filter, ac->data, ac->datatype);
1486         }
1487         
1488         /* filter data that we're working on 
1489          * - hierarchy matters if we're doing this from the channels region
1490          *   since we only want to apply this to channels we can "see", 
1491          *   and have these affect their relatives
1492          * - but for Graph Editor, this gets used also from main region
1493          *   where hierarchy doesn't apply [#21276]
1494          */
1495         if ((ac->spacetype == SPACE_IPO) && (ac->regiontype != RGN_TYPE_CHANNELS)) {
1496                 /* graph editor (case 2) */
1497                 filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_CHANNELS | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_NODUPLIS);
1498         }
1499         else {
1500                 /* standard case */
1501                 filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_LIST_CHANNELS | ANIMFILTER_NODUPLIS);
1502         }
1503         if (onlysel) filter |= ANIMFILTER_SEL;
1504         ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
1505         
1506         /* if toggling, check if disable or enable */
1507         if (mode == ACHANNEL_SETFLAG_TOGGLE) {
1508                 /* default to turn all on, unless we encounter one that's on... */
1509                 mode = ACHANNEL_SETFLAG_ADD;
1510                 
1511                 /* see if we should turn off instead... */
1512                 for (ale = anim_data.first; ale; ale = ale->next) {
1513                         /* set the setting in the appropriate way (if available) */
1514                         if (ANIM_channel_setting_get(ac, ale, setting) > 0) {
1515                                 mode = ACHANNEL_SETFLAG_CLEAR;
1516                                 break;
1517                         }
1518                 }
1519         }
1520         
1521         /* apply the setting */
1522         for (ale = anim_data.first; ale; ale = ale->next) {
1523                 /* skip channel if setting is not available */
1524                 if (ANIM_channel_setting_get(ac, ale, setting) == -1)
1525                         continue;
1526                 
1527                 /* set the setting in the appropriate way */
1528                 ANIM_channel_setting_set(ac, ale, setting, mode);
1529                 
1530                 /* if flush status... */
1531                 if (flush)
1532                         ANIM_flush_setting_anim_channels(ac, &all_data, ale, setting, mode);
1533         }
1534         
1535         BLI_freelistN(&anim_data);
1536         BLI_freelistN(&all_data);
1537 }
1538
1539 /* ------------------- */
1540
1541 static int animchannels_setflag_exec(bContext *C, wmOperator *op)
1542 {
1543         bAnimContext ac;
1544         short mode, setting;
1545         short flush = 1;
1546         
1547         /* get editor data */
1548         if (ANIM_animdata_get_context(C, &ac) == 0)
1549                 return OPERATOR_CANCELLED;
1550                 
1551         /* mode (eAnimChannels_SetFlag), setting (eAnimChannel_Settings) */
1552         mode = RNA_enum_get(op->ptr, "mode");
1553         setting = RNA_enum_get(op->ptr, "type");
1554         
1555         /* check if setting is flushable */
1556         if (setting == ACHANNEL_SETTING_EXPAND)
1557                 flush = 0;
1558         
1559         /* modify setting 
1560          *      - only selected channels are affected
1561          */
1562         setflag_anim_channels(&ac, setting, mode, 1, flush);
1563         
1564         /* send notifier that things have changed */
1565         WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL);
1566         
1567         return OPERATOR_FINISHED;
1568 }
1569
1570 /* duplicate of 'ANIM_OT_channels_setting_toggle' for menu title only, weak! */
1571 static void ANIM_OT_channels_setting_enable(wmOperatorType *ot)
1572 {
1573         PropertyRNA *prop;
1574         
1575         /* identifiers */
1576         ot->name = "Enable Channel Setting";
1577         ot->idname = "ANIM_OT_channels_setting_enable";
1578         ot->description = "Enable specified setting on all selected animation channels";
1579         
1580         /* api callbacks */
1581         ot->invoke = WM_menu_invoke;
1582         ot->exec = animchannels_setflag_exec;
1583         ot->poll = animedit_poll_channels_active;
1584         
1585         /* flags */
1586         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1587         
1588         /* props */
1589         /* flag-setting mode */
1590         prop = RNA_def_enum(ot->srna, "mode", prop_animchannel_setflag_types, ACHANNEL_SETFLAG_ADD, "Mode", "");
1591         RNA_def_property_flag(prop, PROP_HIDDEN);
1592         /* setting to set */
1593         ot->prop = RNA_def_enum(ot->srna, "type", prop_animchannel_settings_types, 0, "Type", "");
1594 }
1595 /* duplicate of 'ANIM_OT_channels_setting_toggle' for menu title only, weak! */
1596 static void ANIM_OT_channels_setting_disable(wmOperatorType *ot)
1597 {
1598         PropertyRNA *prop;
1599         
1600         /* identifiers */
1601         ot->name = "Disable Channel Setting";
1602         ot->idname = "ANIM_OT_channels_setting_disable";
1603         ot->description = "Disable specified setting on all selected animation channels";
1604         
1605         /* api callbacks */
1606         ot->invoke = WM_menu_invoke;
1607         ot->exec = animchannels_setflag_exec;
1608         ot->poll = animedit_poll_channels_active;
1609         
1610         /* flags */
1611         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1612         
1613         /* props */
1614         /* flag-setting mode */
1615         prop = RNA_def_enum(ot->srna, "mode", prop_animchannel_setflag_types, ACHANNEL_SETFLAG_CLEAR, "Mode", "");
1616         RNA_def_property_flag(prop, PROP_HIDDEN); /* internal hack - don't expose */
1617         /* setting to set */
1618         ot->prop = RNA_def_enum(ot->srna, "type", prop_animchannel_settings_types, 0, "Type", "");
1619 }
1620
1621 static void ANIM_OT_channels_setting_toggle(wmOperatorType *ot)
1622 {
1623         PropertyRNA *prop;
1624         
1625         /* identifiers */
1626         ot->name = "Toggle Channel Setting";
1627         ot->idname = "ANIM_OT_channels_setting_toggle";
1628         ot->description = "Toggle specified setting on all selected animation channels";
1629         
1630         /* api callbacks */
1631         ot->invoke = WM_menu_invoke;
1632         ot->exec = animchannels_setflag_exec;
1633         ot->poll = animedit_poll_channels_active;
1634         
1635         /* flags */
1636         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1637         
1638         /* props */
1639         /* flag-setting mode */
1640         prop = RNA_def_enum(ot->srna, "mode", prop_animchannel_setflag_types, ACHANNEL_SETFLAG_TOGGLE, "Mode", "");
1641         RNA_def_property_flag(prop, PROP_HIDDEN); /* internal hack - don't expose */
1642         /* setting to set */
1643         ot->prop = RNA_def_enum(ot->srna, "type", prop_animchannel_settings_types, 0, "Type", "");
1644 }
1645
1646 static void ANIM_OT_channels_editable_toggle(wmOperatorType *ot)
1647 {
1648         PropertyRNA *prop;
1649         
1650         /* identifiers */
1651         ot->name = "Toggle Channel Editability";
1652         ot->idname = "ANIM_OT_channels_editable_toggle";
1653         ot->description = "Toggle editability of selected channels";
1654         
1655         /* api callbacks */
1656         ot->exec = animchannels_setflag_exec;
1657         ot->poll = animedit_poll_channels_active;
1658         
1659         /* flags */
1660         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1661         
1662         /* props */
1663         /* flag-setting mode */
1664         RNA_def_enum(ot->srna, "mode", prop_animchannel_setflag_types, ACHANNEL_SETFLAG_TOGGLE, "Mode", "");
1665         /* setting to set */
1666         prop = RNA_def_enum(ot->srna, "type", prop_animchannel_settings_types, ACHANNEL_SETTING_PROTECT, "Type", "");
1667         RNA_def_property_flag(prop, PROP_HIDDEN); /* internal hack - don't expose */
1668 }
1669
1670 /* ********************** Expand Channels Operator *********************** */
1671
1672 static int animchannels_expand_exec(bContext *C, wmOperator *op)
1673 {
1674         bAnimContext ac;
1675         short onlysel = 1;
1676         
1677         /* get editor data */
1678         if (ANIM_animdata_get_context(C, &ac) == 0)
1679                 return OPERATOR_CANCELLED;
1680                 
1681         /* only affect selected channels? */
1682         if (RNA_boolean_get(op->ptr, "all"))
1683                 onlysel = 0;
1684         
1685         /* modify setting */
1686         setflag_anim_channels(&ac, ACHANNEL_SETTING_EXPAND, ACHANNEL_SETFLAG_ADD, onlysel, 0);
1687         
1688         /* send notifier that things have changed */
1689         WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL);
1690         
1691         return OPERATOR_FINISHED;
1692 }
1693
1694 static void ANIM_OT_channels_expand(wmOperatorType *ot)
1695 {
1696         /* identifiers */
1697         ot->name = "Expand Channels";
1698         ot->idname = "ANIM_OT_channels_expand";
1699         ot->description = "Expand (i.e. open) all selected expandable animation channels";
1700         
1701         /* api callbacks */
1702         ot->exec = animchannels_expand_exec;
1703         ot->poll = animedit_poll_channels_active;
1704         
1705         /* flags */
1706         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1707         
1708         /* props */
1709         ot->prop = RNA_def_boolean(ot->srna, "all", 1, "All", "Expand all channels (not just selected ones)");
1710 }
1711
1712 /* ********************** Collapse Channels Operator *********************** */
1713
1714 static int animchannels_collapse_exec(bContext *C, wmOperator *op)
1715 {
1716         bAnimContext ac;
1717         short onlysel = 1;
1718         
1719         /* get editor data */
1720         if (ANIM_animdata_get_context(C, &ac) == 0)
1721                 return OPERATOR_CANCELLED;
1722                 
1723         /* only affect selected channels? */
1724         if (RNA_boolean_get(op->ptr, "all"))
1725                 onlysel = 0;
1726         
1727         /* modify setting */
1728         setflag_anim_channels(&ac, ACHANNEL_SETTING_EXPAND, ACHANNEL_SETFLAG_CLEAR, onlysel, 0);
1729         
1730         /* send notifier that things have changed */
1731         WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL);
1732         
1733         return OPERATOR_FINISHED;
1734 }
1735
1736 static void ANIM_OT_channels_collapse(wmOperatorType *ot)
1737 {
1738         /* identifiers */
1739         ot->name = "Collapse Channels";
1740         ot->idname = "ANIM_OT_channels_collapse";
1741         ot->description = "Collapse (i.e. close) all selected expandable animation channels";
1742         
1743         /* api callbacks */
1744         ot->exec = animchannels_collapse_exec;
1745         ot->poll = animedit_poll_channels_active;
1746         
1747         /* flags */
1748         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1749         
1750         /* props */
1751         ot->prop = RNA_def_boolean(ot->srna, "all", 1, "All", "Collapse all channels (not just selected ones)");
1752 }
1753
1754 /* ******************* Reenable Disabled Operator ******************* */
1755
1756 static int animchannels_enable_poll(bContext *C)
1757 {
1758         ScrArea *sa = CTX_wm_area(C);
1759
1760         /* channels region test */
1761         /* TODO: could enhance with actually testing if channels region? */
1762         if (ELEM(NULL, sa, CTX_wm_region(C)))
1763                 return 0;
1764                 
1765         /* animation editor test - Action/Dopesheet/etc. and Graph only */
1766         if (ELEM(sa->spacetype, SPACE_ACTION, SPACE_IPO) == 0)
1767                 return 0;
1768                 
1769         return 1;
1770 }
1771
1772 static int animchannels_enable_exec(bContext *C, wmOperator *UNUSED(op))
1773 {
1774         bAnimContext ac;
1775         
1776         ListBase anim_data = {NULL, NULL};
1777         bAnimListElem *ale;
1778         int filter;
1779         
1780         /* get editor data */
1781         if (ANIM_animdata_get_context(C, &ac) == 0)
1782                 return OPERATOR_CANCELLED;
1783         
1784         /* filter data */
1785         filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_NODUPLIS);
1786         ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
1787         
1788         /* loop through filtered data and clean curves */
1789         for (ale = anim_data.first; ale; ale = ale->next) {
1790                 FCurve *fcu = (FCurve *)ale->data;
1791                 
1792                 /* remove disabled flags from F-Curves */
1793                 fcu->flag &= ~FCURVE_DISABLED;
1794                 
1795                 /* for drivers, let's do the same too */
1796                 if (fcu->driver)
1797                         fcu->driver->flag &= ~DRIVER_FLAG_INVALID;
1798                         
1799                 /* tag everything for updates - in particular, this is needed to get drivers working again */
1800                 ANIM_list_elem_update(ac.scene, ale);
1801         }
1802         
1803         /* free temp data */
1804         BLI_freelistN(&anim_data);
1805                 
1806         /* send notifier that things have changed */
1807         WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL);
1808         
1809         return OPERATOR_FINISHED;
1810 }
1811
1812 static void ANIM_OT_channels_fcurves_enable(wmOperatorType *ot)
1813 {
1814         /* identifiers */
1815         ot->name = "Revive Disabled F-Curves";
1816         ot->idname = "ANIM_OT_channels_fcurves_enable";
1817         ot->description = "Clears 'disabled' tag from all F-Curves to get broken F-Curves working again";
1818         
1819         /* api callbacks */
1820         ot->exec = animchannels_enable_exec;
1821         ot->poll = animchannels_enable_poll;
1822         
1823         /* flags */
1824         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1825 }
1826
1827 /* ********************** Select All Operator *********************** */
1828
1829 static int animchannels_deselectall_exec(bContext *C, wmOperator *op)
1830 {
1831         bAnimContext ac;
1832         
1833         /* get editor data */
1834         if (ANIM_animdata_get_context(C, &ac) == 0)
1835                 return OPERATOR_CANCELLED;
1836                 
1837         /* 'standard' behavior - check if selected, then apply relevant selection */
1838         if (RNA_boolean_get(op->ptr, "invert"))
1839                 ANIM_deselect_anim_channels(&ac, ac.data, ac.datatype, 0, ACHANNEL_SETFLAG_TOGGLE);
1840         else
1841                 ANIM_deselect_anim_channels(&ac, ac.data, ac.datatype, 1, ACHANNEL_SETFLAG_ADD);
1842         
1843         /* send notifier that things have changed */
1844         WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_SELECTED, NULL);
1845         
1846         return OPERATOR_FINISHED;
1847 }
1848  
1849 static void ANIM_OT_channels_select_all_toggle(wmOperatorType *ot)
1850 {
1851         /* identifiers */
1852         ot->name = "Select All";
1853         ot->idname = "ANIM_OT_channels_select_all_toggle";
1854         ot->description = "Toggle selection of all animation channels";
1855         
1856         /* api callbacks */
1857         ot->exec = animchannels_deselectall_exec;
1858         ot->poll = animedit_poll_channels_nla_tweakmode_off;
1859         
1860         /* flags */
1861         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1862         
1863         /* props */
1864         ot->prop = RNA_def_boolean(ot->srna, "invert", 0, "Invert", "");
1865 }
1866
1867 /* ******************** Borderselect Operator *********************** */
1868
1869 static void borderselect_anim_channels(bAnimContext *ac, rcti *rect, short selectmode)
1870 {
1871         ListBase anim_data = {NULL, NULL};
1872         bAnimListElem *ale;
1873         int filter;
1874         
1875         SpaceNla *snla = (SpaceNla *)ac->sl;
1876         View2D *v2d = &ac->ar->v2d;
1877         rctf rectf;
1878         float ymin, ymax;
1879         
1880         /* set initial y extents */
1881         if (ac->datatype == ANIMCONT_NLA) {
1882                 ymin = (float)(-NLACHANNEL_HEIGHT(snla));
1883                 ymax = 0.0f;
1884         }
1885         else {
1886                 ymin = 0.0f;
1887                 ymax = (float)(-ACHANNEL_HEIGHT);
1888         }
1889         
1890         /* convert border-region to view coordinates */
1891         UI_view2d_region_to_view(v2d, rect->xmin, rect->ymin + 2, &rectf.xmin, &rectf.ymin);
1892         UI_view2d_region_to_view(v2d, rect->xmax, rect->ymax - 2, &rectf.xmax, &rectf.ymax);
1893         
1894         /* filter data */
1895         filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_LIST_CHANNELS);
1896         ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
1897         
1898         /* loop over data, doing border select */
1899         for (ale = anim_data.first; ale; ale = ale->next) {
1900                 if (ac->datatype == ANIMCONT_NLA)
1901                         ymin = ymax - NLACHANNEL_STEP(snla);
1902                 else
1903                         ymin = ymax - ACHANNEL_STEP;
1904                 
1905                 /* if channel is within border-select region, alter it */
1906                 if (!((ymax < rectf.ymin) || (ymin > rectf.ymax))) {
1907                         /* set selection flags only */
1908                         ANIM_channel_setting_set(ac, ale, ACHANNEL_SETTING_SELECT, selectmode);
1909                         
1910                         /* type specific actions */
1911                         switch (ale->type) {
1912                                 case ANIMTYPE_GROUP:
1913                                 {
1914                                         bActionGroup *agrp = (bActionGroup *)ale->data;
1915                                         
1916                                         /* always clear active flag after doing this */
1917                                         agrp->flag &= ~AGRP_ACTIVE;
1918                                 }
1919                                 break;
1920                                 case ANIMTYPE_NLATRACK:
1921                                 {
1922                                         NlaTrack *nlt = (NlaTrack *)ale->data;
1923                                         
1924                                         /* for now, it's easier just to do this here manually, as defining a new type 
1925                                          * currently adds complications when doing other stuff 
1926                                          */
1927                                         ACHANNEL_SET_FLAG(nlt, selectmode, NLATRACK_SELECTED);
1928                                 }
1929                                 break;
1930                         }
1931                 }
1932                 
1933                 /* set minimum extent to be the maximum of the next channel */
1934                 ymax = ymin;
1935         }
1936         
1937         /* cleanup */
1938         BLI_freelistN(&anim_data);
1939 }
1940
1941 /* ------------------- */
1942
1943 static int animchannels_borderselect_exec(bContext *C, wmOperator *op)
1944 {
1945         bAnimContext ac;
1946         rcti rect;
1947         short selectmode = 0;
1948         int gesture_mode, extend;
1949         
1950         /* get editor data */
1951         if (ANIM_animdata_get_context(C, &ac) == 0)
1952                 return OPERATOR_CANCELLED;
1953         
1954         /* get settings from operator */
1955         WM_operator_properties_border_to_rcti(op, &rect);
1956         
1957         gesture_mode = RNA_int_get(op->ptr, "gesture_mode");
1958         extend = RNA_boolean_get(op->ptr, "extend");
1959
1960         if (!extend)
1961                 ANIM_deselect_anim_channels(&ac, ac.data, ac.datatype, 1, ACHANNEL_SETFLAG_CLEAR);
1962
1963         if (gesture_mode == GESTURE_MODAL_SELECT)
1964                 selectmode = ACHANNEL_SETFLAG_ADD;
1965         else
1966                 selectmode = ACHANNEL_SETFLAG_CLEAR;
1967         
1968         /* apply borderselect animation channels */
1969         borderselect_anim_channels(&ac, &rect, selectmode);
1970         
1971         /* send notifier that things have changed */
1972         WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_SELECTED, NULL);
1973         
1974         return OPERATOR_FINISHED;
1975
1976
1977 static void ANIM_OT_channels_select_border(wmOperatorType *ot)
1978 {
1979         /* identifiers */
1980         ot->name = "Border Select";
1981         ot->idname = "ANIM_OT_channels_select_border";
1982         ot->description = "Select all animation channels within the specified region";
1983         
1984         /* api callbacks */
1985         ot->invoke = WM_border_select_invoke;
1986         ot->exec = animchannels_borderselect_exec;
1987         ot->modal = WM_border_select_modal;
1988         ot->cancel = WM_border_select_cancel;
1989         
1990         ot->poll = animedit_poll_channels_nla_tweakmode_off;
1991         
1992         /* flags */
1993         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1994         
1995         /* rna */
1996         WM_operator_properties_gesture_border(ot, TRUE);
1997 }
1998
1999 /* ******************* Rename Operator ***************************** */
2000 /* Allow renaming some channels by clicking on them */
2001
2002 static void rename_anim_channels(bAnimContext *ac, int channel_index)
2003 {
2004         ListBase anim_data = {NULL, NULL};
2005         bAnimChannelType *acf;
2006         bAnimListElem *ale;
2007         int filter;
2008         
2009         /* get the channel that was clicked on */
2010         /* filter channels */
2011         filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_LIST_CHANNELS);
2012         ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
2013         
2014         /* get channel from index */
2015         ale = BLI_findlink(&anim_data, channel_index);
2016         if (ale == NULL) {
2017                 /* channel not found */
2018                 if (G.debug & G_DEBUG)
2019                         printf("Error: animation channel (index = %d) not found in rename_anim_channels()\n", channel_index);
2020                 
2021                 BLI_freelistN(&anim_data);
2022                 return;
2023         }
2024         
2025         /* check that channel can be renamed */
2026         acf = ANIM_channel_get_typeinfo(ale);
2027         if (acf && acf->name_prop) {
2028                 PointerRNA ptr;
2029                 PropertyRNA *prop;
2030                 
2031                 /* ok if we can get name property to edit from this channel */
2032                 if (acf->name_prop(ale, &ptr, &prop)) {
2033                         /* actually showing the rename textfield is done on redraw,
2034                          * so here we just store the index of this channel in the 
2035                          * dopesheet data, which will get utilized when drawing the
2036                          * channel...
2037                          *
2038                          * +1 factor is for backwards compat issues
2039                          */
2040                         if (ac->ads) {
2041                                 ac->ads->renameIndex = channel_index + 1;
2042                         }
2043                 }
2044         }
2045         
2046         /* free temp data and tag for refresh */
2047         BLI_freelistN(&anim_data);
2048         ED_region_tag_redraw(ac->ar);
2049 }
2050
2051 static int animchannels_rename_invoke(bContext *C, wmOperator *UNUSED(op), wmEvent *evt)
2052 {
2053         bAnimContext ac;
2054         ARegion *ar;
2055         View2D *v2d;
2056         int channel_index;
2057         float x, y;
2058         
2059         /* get editor data */
2060         if (ANIM_animdata_get_context(C, &ac) == 0)
2061                 return OPERATOR_CANCELLED;
2062                 
2063         /* get useful pointers from animation context data */
2064         ar = ac.ar;
2065         v2d = &ar->v2d;
2066         
2067         /* figure out which channel user clicked in 
2068          * Note: although channels technically start at (y = ACHANNEL_FIRST), we need to adjust by half a channel's height
2069          *              so that the tops of channels get caught ok. Since ACHANNEL_FIRST is really ACHANNEL_HEIGHT, we simply use
2070          *              ACHANNEL_HEIGHT_HALF.
2071          */
2072         UI_view2d_region_to_view(v2d, evt->mval[0], evt->mval[1], &x, &y);
2073         
2074         if (ac.datatype == ANIMCONT_NLA) {
2075                 SpaceNla *snla = (SpaceNla *)ac.sl;
2076                 UI_view2d_listview_view_to_cell(v2d, NLACHANNEL_NAMEWIDTH, NLACHANNEL_STEP(snla), 0, (float)NLACHANNEL_HEIGHT_HALF(snla), x, y, NULL, &channel_index);
2077         }
2078         else {
2079                 UI_view2d_listview_view_to_cell(v2d, ACHANNEL_NAMEWIDTH, ACHANNEL_STEP, 0, (float)ACHANNEL_HEIGHT_HALF, x, y, NULL, &channel_index);
2080         }
2081         
2082         /* handle click */
2083         rename_anim_channels(&ac, channel_index);
2084         
2085         return OPERATOR_FINISHED;
2086 }
2087
2088 static void ANIM_OT_channels_rename(wmOperatorType *ot)
2089 {
2090         /* identifiers */
2091         ot->name = "Rename Channels";
2092         ot->idname = "ANIM_OT_channels_rename";
2093         ot->description = "Rename animation channel under mouse";
2094         
2095         /* api callbacks */
2096         ot->invoke = animchannels_rename_invoke;
2097         ot->poll = animedit_poll_channels_active;
2098 }
2099
2100 /* ******************** Mouse-Click Operator *********************** */
2101 /* Handle selection changes due to clicking on channels. Settings will get caught by UI code... */
2102
2103 static int mouse_anim_channels(bAnimContext *ac, float UNUSED(x), int channel_index, short selectmode)
2104 {
2105         ListBase anim_data = {NULL, NULL};
2106         bAnimListElem *ale;
2107         int filter;
2108         int notifierFlags = 0;
2109         
2110         /* get the channel that was clicked on */
2111         /* filter channels */
2112         filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_LIST_CHANNELS);
2113         ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
2114         
2115         /* get channel from index */
2116         ale = BLI_findlink(&anim_data, channel_index);
2117         if (ale == NULL) {
2118                 /* channel not found */
2119                 if (G.debug & G_DEBUG)
2120                         printf("Error: animation channel (index = %d) not found in mouse_anim_channels()\n", channel_index);
2121                 
2122                 BLI_freelistN(&anim_data);
2123                 return 0;
2124         }
2125
2126         /* selectmode -1 is a special case for ActionGroups only, which selects all of the channels underneath it only... */
2127         /* TODO: should this feature be extended to work with other channel types too? */
2128         if ((selectmode == -1) && (ale->type != ANIMTYPE_GROUP)) {
2129                 /* normal channels should not behave normally in this case */
2130                 BLI_freelistN(&anim_data);
2131                 return 0;
2132         }
2133
2134         /* action to take depends on what channel we've got */
2135         /* WARNING: must keep this in sync with the equivalent function in nla_channels.c */
2136         switch (ale->type) {
2137                 case ANIMTYPE_SCENE:
2138                 {
2139                         Scene *sce = (Scene *)ale->data;
2140                         AnimData *adt = sce->adt;
2141                         
2142                         /* set selection status */
2143                         if (selectmode == SELECT_INVERT) {
2144                                 /* swap select */
2145                                 sce->flag ^= SCE_DS_SELECTED;
2146                                 if (adt) adt->flag ^= ADT_UI_SELECTED;
2147                         }
2148                         else {
2149                                 sce->flag |= SCE_DS_SELECTED;
2150                                 if (adt) adt->flag |= ADT_UI_SELECTED;
2151                         }
2152                         
2153                         notifierFlags |= (ND_ANIMCHAN | NA_SELECTED);
2154                 }
2155                 break;
2156                 case ANIMTYPE_OBJECT:
2157                 {
2158                         bDopeSheet *ads = (bDopeSheet *)ac->data;
2159                         Scene *sce = (Scene *)ads->source;
2160                         Base *base = (Base *)ale->data;
2161                         Object *ob = base->object;
2162                         AnimData *adt = ob->adt;
2163                         
2164                         /* set selection status */
2165                         if (selectmode == SELECT_INVERT) {
2166                                 /* swap select */
2167                                 base->flag ^= SELECT;
2168                                 ob->flag = base->flag;
2169                                 
2170                                 if (adt) adt->flag ^= ADT_UI_SELECTED;
2171                         }
2172                         else {
2173                                 Base *b;
2174                                 
2175                                 /* deselect all */
2176                                 /* TODO: should this deselect all other types of channels too? */
2177                                 for (b = sce->base.first; b; b = b->next) {
2178                                         b->flag &= ~SELECT;
2179                                         b->object->flag = b->flag;
2180                                         if (b->object->adt) b->object->adt->flag &= ~(ADT_UI_SELECTED | ADT_UI_ACTIVE);
2181                                 }
2182                                 
2183                                 /* select object now */
2184                                 base->flag |= SELECT;
2185                                 ob->flag |= SELECT;
2186                                 if (adt) adt->flag |= ADT_UI_SELECTED;
2187                         }
2188                         
2189                         if ((adt) && (adt->flag & ADT_UI_SELECTED))
2190                                 adt->flag |= ADT_UI_ACTIVE;
2191                         
2192                         notifierFlags |= (ND_ANIMCHAN | NA_SELECTED);
2193                 }
2194                 break;
2195                 
2196                 case ANIMTYPE_FILLACTD: /* Action Expander */
2197                 case ANIMTYPE_DSMAT:    /* Datablock AnimData Expanders */
2198                 case ANIMTYPE_DSLAM:
2199                 case ANIMTYPE_DSCAM:
2200                 case ANIMTYPE_DSCUR:
2201                 case ANIMTYPE_DSSKEY:
2202                 case ANIMTYPE_DSWOR:
2203                 case ANIMTYPE_DSPART:
2204                 case ANIMTYPE_DSMBALL:
2205                 case ANIMTYPE_DSARM:
2206                 case ANIMTYPE_DSMESH:
2207                 case ANIMTYPE_DSNTREE:
2208                 case ANIMTYPE_DSTEX:
2209                 case ANIMTYPE_DSLAT:
2210                 case ANIMTYPE_DSSPK:
2211                 {
2212                         /* sanity checking... */
2213                         if (ale->adt) {
2214                                 /* select/deselect */
2215                                 if (selectmode == SELECT_INVERT) {
2216                                         /* inverse selection status of this AnimData block only */
2217                                         ale->adt->flag ^= ADT_UI_SELECTED;
2218                                 }
2219                                 else {
2220                                         /* select AnimData block by itself */
2221                                         ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, 0, ACHANNEL_SETFLAG_CLEAR);
2222                                         ale->adt->flag |= ADT_UI_SELECTED;
2223                                 }
2224                                 
2225                                 /* set active? */
2226                                 if ((ale->adt) && (ale->adt->flag & ADT_UI_SELECTED))
2227                                         ale->adt->flag |= ADT_UI_ACTIVE;
2228                         }
2229                         
2230                         notifierFlags |= (ND_ANIMCHAN | NA_SELECTED);
2231         }
2232                 break;
2233                 
2234                 case ANIMTYPE_GROUP: 
2235                 {
2236                         bActionGroup *agrp = (bActionGroup *)ale->data;
2237                         
2238                         /* select/deselect group */
2239                         if (selectmode == SELECT_INVERT) {
2240                                 /* inverse selection status of this group only */
2241                                 agrp->flag ^= AGRP_SELECTED;
2242                         }
2243                         else if (selectmode == -1) {
2244                                 /* select all in group (and deselect everthing else) */
2245                                 FCurve *fcu;
2246                                 
2247                                 /* deselect all other channels */
2248                                 ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, 0, ACHANNEL_SETFLAG_CLEAR);
2249                                 
2250                                 /* only select channels in group and group itself */
2251                                 for (fcu = agrp->channels.first; fcu && fcu->grp == agrp; fcu = fcu->next)
2252                                         fcu->flag |= FCURVE_SELECTED;
2253                                 agrp->flag |= AGRP_SELECTED;
2254                         }
2255                         else {
2256                                 /* select group by itself */
2257                                 ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, 0, ACHANNEL_SETFLAG_CLEAR);
2258                                 agrp->flag |= AGRP_SELECTED;
2259                         }
2260                         
2261                         /* if group is selected now, make group the 'active' one in the visible list */
2262                         if (agrp->flag & AGRP_SELECTED)
2263                                 ANIM_set_active_channel(ac, ac->data, ac->datatype, filter, agrp, ANIMTYPE_GROUP);
2264                         else
2265                                 ANIM_set_active_channel(ac, ac->data, ac->datatype, filter, NULL, ANIMTYPE_GROUP);
2266                                 
2267                         notifierFlags |= (ND_ANIMCHAN | NA_SELECTED);
2268                 }
2269                 break;
2270                 case ANIMTYPE_FCURVE: 
2271                 {
2272                         FCurve *fcu = (FCurve *)ale->data;
2273                         
2274                         /* select/deselect */
2275                         if (selectmode == SELECT_INVERT) {
2276                                 /* inverse selection status of this F-Curve only */
2277                                 fcu->flag ^= FCURVE_SELECTED;
2278                         }
2279                         else {
2280                                 /* select F-Curve by itself */
2281                                 ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, 0, ACHANNEL_SETFLAG_CLEAR);
2282                                 fcu->flag |= FCURVE_SELECTED;
2283                         }
2284                         
2285                         /* if F-Curve is selected now, make F-Curve the 'active' one in the visible list */
2286                         if (fcu->flag & FCURVE_SELECTED)
2287                                 ANIM_set_active_channel(ac, ac->data, ac->datatype, filter, fcu, ANIMTYPE_FCURVE);
2288                                 
2289                         notifierFlags |= (ND_ANIMCHAN | NA_SELECTED);
2290                 }
2291                 break;
2292                 case ANIMTYPE_SHAPEKEY: 
2293                 {
2294                         KeyBlock *kb = (KeyBlock *)ale->data;
2295                         
2296                         /* select/deselect */
2297                         if (selectmode == SELECT_INVERT) {
2298                                 /* inverse selection status of this ShapeKey only */
2299                                 kb->flag ^= KEYBLOCK_SEL;
2300                         }
2301                         else {
2302                                 /* select ShapeKey by itself */
2303                                 ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, 0, ACHANNEL_SETFLAG_CLEAR);
2304                                 kb->flag |= KEYBLOCK_SEL;
2305                         }
2306                                 
2307                         notifierFlags |= (ND_ANIMCHAN | NA_SELECTED);
2308                 }
2309                 break;
2310                 case ANIMTYPE_GPDATABLOCK:
2311                 {
2312                         bGPdata *gpd = (bGPdata *)ale->data;
2313                         
2314                         /* toggle expand 
2315                          *      - although the triangle widget already allows this, the whole channel can also be used for this purpose
2316                          */
2317                         gpd->flag ^= GP_DATA_EXPAND;
2318                         
2319                         notifierFlags |= (ND_ANIMCHAN | NA_EDITED);
2320                 }
2321                 break;
2322                 case ANIMTYPE_GPLAYER:
2323                 {
2324                         bGPDlayer *gpl = (bGPDlayer *)ale->data;
2325                         
2326                         /* select/deselect */
2327                         if (selectmode == SELECT_INVERT) {
2328                                 /* invert selection status of this layer only */
2329                                 gpl->flag ^= GP_LAYER_SELECT;
2330                         }
2331                         else {
2332                                 /* select layer by itself */
2333                                 ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, 0, ACHANNEL_SETFLAG_CLEAR);
2334                                 gpl->flag |= GP_LAYER_SELECT;
2335                         }
2336                         
2337                         notifierFlags |= (ND_ANIMCHAN | NA_EDITED);
2338                 }
2339                 break;
2340                 case ANIMTYPE_MASKDATABLOCK:
2341                 {
2342                         Mask *mask = (Mask *)ale->data;
2343                         
2344                         /* toggle expand
2345                          *      - although the triangle widget already allows this, the whole channel can also be used for this purpose
2346                          */
2347                         mask->flag ^= MASK_ANIMF_EXPAND;
2348                         
2349                         notifierFlags |= (ND_ANIMCHAN | NA_EDITED);
2350                 }
2351                 break;
2352                 case ANIMTYPE_MASKLAYER:
2353                 {
2354                         MaskLayer *masklay = (MaskLayer *)ale->data;
2355                         
2356                         /* select/deselect */
2357                         if (selectmode == SELECT_INVERT) {
2358                                 /* invert selection status of this layer only */
2359                                 masklay->flag ^= MASK_LAYERFLAG_SELECT;
2360                         }
2361                         else {
2362                                 /* select layer by itself */
2363                                 ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, 0, ACHANNEL_SETFLAG_CLEAR);
2364                                 masklay->flag |= MASK_LAYERFLAG_SELECT;
2365                         }
2366                         
2367                         notifierFlags |= (ND_ANIMCHAN | NA_EDITED);
2368                 }
2369                 break;
2370                 default:
2371                         if (G.debug & G_DEBUG)
2372                                 printf("Error: Invalid channel type in mouse_anim_channels()\n");
2373         }
2374         
2375         /* free channels */
2376         BLI_freelistN(&anim_data);
2377         
2378         /* return notifier flags */
2379         return notifierFlags;
2380 }
2381
2382 /* ------------------- */
2383
2384 /* handle clicking */
2385 static int animchannels_mouseclick_invoke(bContext *C, wmOperator *op, wmEvent *event)
2386 {
2387         bAnimContext ac;
2388         ARegion *ar;
2389         View2D *v2d;
2390         int channel_index;
2391         int notifierFlags = 0;
2392         short selectmode;
2393         float x, y;
2394         
2395         
2396         /* get editor data */
2397         if (ANIM_animdata_get_context(C, &ac) == 0)
2398                 return OPERATOR_CANCELLED;
2399                 
2400         /* get useful pointers from animation context data */
2401         ar = ac.ar;
2402         v2d = &ar->v2d;
2403         
2404         /* select mode is either replace (deselect all, then add) or add/extend */
2405         if (RNA_boolean_get(op->ptr, "extend"))
2406                 selectmode = SELECT_INVERT;
2407         else if (RNA_boolean_get(op->ptr, "children_only"))
2408                 selectmode = -1;  /* this is a bit of a special case for ActionGroups only... should it be removed or extended to all instead? */
2409         else
2410                 selectmode = SELECT_REPLACE;
2411         
2412         /* figure out which channel user clicked in 
2413          * Note: although channels technically start at (y = ACHANNEL_FIRST), we need to adjust by half a channel's height
2414          *              so that the tops of channels get caught ok. Since ACHANNEL_FIRST is really ACHANNEL_HEIGHT, we simply use
2415          *              ACHANNEL_HEIGHT_HALF.
2416          */
2417         UI_view2d_region_to_view(v2d, event->mval[0], event->mval[1], &x, &y);
2418         UI_view2d_listview_view_to_cell(v2d, ACHANNEL_NAMEWIDTH, ACHANNEL_STEP, 0, (float)ACHANNEL_HEIGHT_HALF, x, y, NULL, &channel_index);
2419         
2420         /* handle mouse-click in the relevant channel then */
2421         notifierFlags = mouse_anim_channels(&ac, x, channel_index, selectmode);
2422         
2423         /* set notifier that things have changed */
2424         WM_event_add_notifier(C, NC_ANIMATION | notifierFlags, NULL);
2425         
2426         return OPERATOR_FINISHED;
2427 }
2428  
2429 static void ANIM_OT_channels_click(wmOperatorType *ot)
2430 {
2431         PropertyRNA *prop;
2432         
2433         /* identifiers */
2434         ot->name = "Mouse Click on Channels";
2435         ot->idname = "ANIM_OT_channels_click";
2436         ot->description = "Handle mouse-clicks over animation channels";
2437         
2438         /* api callbacks */
2439         ot->invoke = animchannels_mouseclick_invoke;
2440         ot->poll = animedit_poll_channels_active;
2441         
2442         /* flags */
2443         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2444         
2445         /* properties */
2446         /* NOTE: don't save settings, otherwise, can end up with some weird behaviour (sticky extend) */
2447         prop = RNA_def_boolean(ot->srna, "extend", 0, "Extend Select", ""); // SHIFTKEY
2448         RNA_def_property_flag(prop, PROP_SKIP_SAVE);
2449         
2450         prop = RNA_def_boolean(ot->srna, "children_only", 0, "Select Children Only", ""); // CTRLKEY|SHIFTKEY
2451         RNA_def_property_flag(prop, PROP_SKIP_SAVE);
2452 }
2453
2454 /* ************************************************************************** */
2455 /* Operator Registration */
2456
2457 void ED_operatortypes_animchannels(void)
2458 {
2459         WM_operatortype_append(ANIM_OT_channels_select_all_toggle);
2460         WM_operatortype_append(ANIM_OT_channels_select_border);
2461         
2462         WM_operatortype_append(ANIM_OT_channels_click);
2463         WM_operatortype_append(ANIM_OT_channels_rename);
2464         
2465         WM_operatortype_append(ANIM_OT_channels_setting_enable);
2466         WM_operatortype_append(ANIM_OT_channels_setting_disable);
2467         WM_operatortype_append(ANIM_OT_channels_setting_toggle);
2468
2469         WM_operatortype_append(ANIM_OT_channels_delete);
2470
2471         /* XXX does this need to be a separate operator? */
2472         WM_operatortype_append(ANIM_OT_channels_editable_toggle);
2473         
2474         WM_operatortype_append(ANIM_OT_channels_move);
2475         
2476         WM_operatortype_append(ANIM_OT_channels_expand);
2477         WM_operatortype_append(ANIM_OT_channels_collapse);
2478         
2479         WM_operatortype_append(ANIM_OT_channels_visibility_toggle);
2480         WM_operatortype_append(ANIM_OT_channels_visibility_set);
2481         
2482         WM_operatortype_append(ANIM_OT_channels_fcurves_enable);
2483 }
2484
2485 // TODO: check on a poll callback for this, to get hotkeys into menus
2486 void ED_keymap_animchannels(wmKeyConfig *keyconf)
2487 {
2488         wmKeyMap *keymap = WM_keymap_find(keyconf, "Animation Channels", 0, 0);
2489         wmKeyMapItem *kmi;
2490
2491         /* selection */
2492         /* click-select */
2493         /* XXX for now, only leftmouse.... */
2494         WM_keymap_add_item(keymap, "ANIM_OT_channels_click", LEFTMOUSE, KM_PRESS, 0, 0);
2495         RNA_boolean_set(WM_keymap_add_item(keymap, "ANIM_OT_channels_click", LEFTMOUSE, KM_PRESS, KM_SHIFT, 0)->ptr, "extend", TRUE);
2496         RNA_boolean_set(WM_keymap_add_item(keymap, "ANIM_OT_channels_click", LEFTMOUSE, KM_PRESS, KM_CTRL | KM_SHIFT, 0)->ptr, "children_only", TRUE);
2497
2498         /* rename */
2499         WM_keymap_add_item(keymap, "ANIM_OT_channels_rename", LEFTMOUSE, KM_PRESS, KM_CTRL, 0);
2500         
2501         /* deselect all */
2502         WM_keymap_add_item(keymap, "ANIM_OT_channels_select_all_toggle", AKEY, KM_PRESS, 0, 0);
2503         RNA_boolean_set(WM_keymap_add_item(keymap, "ANIM_OT_channels_select_all_toggle", IKEY, KM_PRESS, KM_CTRL, 0)->ptr, "invert", TRUE);
2504         
2505         /* borderselect */
2506         WM_keymap_add_item(keymap, "ANIM_OT_channels_select_border", BKEY, KM_PRESS, 0, 0);
2507         WM_keymap_add_item(keymap, "ANIM_OT_channels_select_border", EVT_TWEAK_L, KM_ANY, 0, 0);
2508         
2509         /* delete */
2510         WM_keymap_add_item(keymap, "ANIM_OT_channels_delete", XKEY, KM_PRESS, 0, 0);
2511         WM_keymap_add_item(keymap, "ANIM_OT_channels_delete", DELKEY, KM_PRESS, 0, 0);
2512         
2513         /* settings */
2514         WM_keymap_add_item(keymap, "ANIM_OT_channels_setting_toggle", WKEY, KM_PRESS, KM_SHIFT, 0);
2515         WM_keymap_add_item(keymap, "ANIM_OT_channels_setting_enable", WKEY, KM_PRESS, KM_CTRL | KM_SHIFT, 0);
2516         WM_keymap_add_item(keymap, "ANIM_OT_channels_setting_disable", WKEY, KM_PRESS, KM_ALT, 0);
2517         
2518         /* settings - specialized hotkeys */
2519         WM_keymap_add_item(keymap, "ANIM_OT_channels_editable_toggle", TABKEY, KM_PRESS, 0, 0);
2520         
2521         /* expand/collapse */
2522         WM_keymap_add_item(keymap, "ANIM_OT_channels_expand", PADPLUSKEY, KM_PRESS, 0, 0);
2523         WM_keymap_add_item(keymap, "ANIM_OT_channels_collapse", PADMINUS, KM_PRESS, 0, 0);
2524         
2525         kmi = WM_keymap_add_item(keymap, "ANIM_OT_channels_expand", PADPLUSKEY, KM_PRESS, KM_CTRL, 0);
2526         RNA_boolean_set(kmi->ptr, "all", FALSE);
2527         kmi = WM_keymap_add_item(keymap, "ANIM_OT_channels_collapse", PADMINUS, KM_PRESS, KM_CTRL, 0);
2528         RNA_boolean_set(kmi->ptr, "all", FALSE);
2529
2530         /* rearranging */
2531         RNA_enum_set(WM_keymap_add_item(keymap, "ANIM_OT_channels_move", PAGEUPKEY, KM_PRESS, 0, 0)->ptr, "direction", REARRANGE_ANIMCHAN_UP);
2532         RNA_enum_set(WM_keymap_add_item(keymap, "ANIM_OT_channels_move", PAGEDOWNKEY, KM_PRESS, 0, 0)->ptr, "direction", REARRANGE_ANIMCHAN_DOWN);
2533         RNA_enum_set(WM_keymap_add_item(keymap, "ANIM_OT_channels_move", PAGEUPKEY, KM_PRESS, KM_SHIFT, 0)->ptr, "direction", REARRANGE_ANIMCHAN_TOP);
2534         RNA_enum_set(WM_keymap_add_item(keymap, "ANIM_OT_channels_move", PAGEDOWNKEY, KM_PRESS, KM_SHIFT, 0)->ptr, "direction", REARRANGE_ANIMCHAN_BOTTOM);
2535         
2536         /* Graph Editor only */
2537         WM_keymap_add_item(keymap, "ANIM_OT_channels_visibility_set", VKEY, KM_PRESS, 0, 0);
2538         WM_keymap_add_item(keymap, "ANIM_OT_channels_visibility_toggle", VKEY, KM_PRESS, KM_SHIFT, 0);
2539 }
2540
2541 /* ************************************************************************** */