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