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