Merging r49681 through r49707 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         rect.xmin = RNA_int_get(op->ptr, "xmin");
1934         rect.ymin = RNA_int_get(op->ptr, "ymin");
1935         rect.xmax = RNA_int_get(op->ptr, "xmax");
1936         rect.ymax = RNA_int_get(op->ptr, "ymax");
1937         
1938         gesture_mode = RNA_int_get(op->ptr, "gesture_mode");
1939         extend = RNA_boolean_get(op->ptr, "extend");
1940
1941         if (!extend)
1942                 ANIM_deselect_anim_channels(&ac, ac.data, ac.datatype, 1, ACHANNEL_SETFLAG_CLEAR);
1943
1944         if (gesture_mode == GESTURE_MODAL_SELECT)
1945                 selectmode = ACHANNEL_SETFLAG_ADD;
1946         else
1947                 selectmode = ACHANNEL_SETFLAG_CLEAR;
1948         
1949         /* apply borderselect animation channels */
1950         borderselect_anim_channels(&ac, &rect, selectmode);
1951         
1952         /* send notifier that things have changed */
1953         WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_SELECTED, NULL);
1954         
1955         return OPERATOR_FINISHED;
1956
1957
1958 static void ANIM_OT_channels_select_border(wmOperatorType *ot)
1959 {
1960         /* identifiers */
1961         ot->name = "Border Select";
1962         ot->idname = "ANIM_OT_channels_select_border";
1963         ot->description = "Select all animation channels within the specified region";
1964         
1965         /* api callbacks */
1966         ot->invoke = WM_border_select_invoke;
1967         ot->exec = animchannels_borderselect_exec;
1968         ot->modal = WM_border_select_modal;
1969         ot->cancel = WM_border_select_cancel;
1970         
1971         ot->poll = animedit_poll_channels_nla_tweakmode_off;
1972         
1973         /* flags */
1974         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1975         
1976         /* rna */
1977         WM_operator_properties_gesture_border(ot, TRUE);
1978 }
1979
1980 /* ******************* Rename Operator ***************************** */
1981 /* Allow renaming some channels by clicking on them */
1982
1983 static void rename_anim_channels(bAnimContext *ac, int channel_index)
1984 {
1985         ListBase anim_data = {NULL, NULL};
1986         bAnimChannelType *acf;
1987         bAnimListElem *ale;
1988         int filter;
1989         
1990         /* get the channel that was clicked on */
1991         /* filter channels */
1992         filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_LIST_CHANNELS);
1993         ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
1994         
1995         /* get channel from index */
1996         ale = BLI_findlink(&anim_data, channel_index);
1997         if (ale == NULL) {
1998                 /* channel not found */
1999                 if (G.debug & G_DEBUG)
2000                         printf("Error: animation channel (index = %d) not found in rename_anim_channels()\n", channel_index);
2001                 
2002                 BLI_freelistN(&anim_data);
2003                 return;
2004         }
2005         
2006         /* check that channel can be renamed */
2007         acf = ANIM_channel_get_typeinfo(ale);
2008         if (acf && acf->name_prop) {
2009                 PointerRNA ptr;
2010                 PropertyRNA *prop;
2011                 
2012                 /* ok if we can get name property to edit from this channel */
2013                 if (acf->name_prop(ale, &ptr, &prop)) {
2014                         /* actually showing the rename textfield is done on redraw,
2015                          * so here we just store the index of this channel in the 
2016                          * dopesheet data, which will get utilized when drawing the
2017                          * channel...
2018                          *
2019                          * +1 factor is for backwards compat issues
2020                          */
2021                         if (ac->ads) {
2022                                 ac->ads->renameIndex = channel_index + 1;
2023                         }
2024                 }
2025         }
2026         
2027         /* free temp data and tag for refresh */
2028         BLI_freelistN(&anim_data);
2029         ED_region_tag_redraw(ac->ar);
2030 }
2031
2032 static int animchannels_rename_invoke(bContext *C, wmOperator *UNUSED(op), wmEvent *evt)
2033 {
2034         bAnimContext ac;
2035         ARegion *ar;
2036         View2D *v2d;
2037         int channel_index;
2038         float x, y;
2039         
2040         /* get editor data */
2041         if (ANIM_animdata_get_context(C, &ac) == 0)
2042                 return OPERATOR_CANCELLED;
2043                 
2044         /* get useful pointers from animation context data */
2045         ar = ac.ar;
2046         v2d = &ar->v2d;
2047         
2048         /* figure out which channel user clicked in 
2049          * Note: although channels technically start at (y = ACHANNEL_FIRST), we need to adjust by half a channel's height
2050          *              so that the tops of channels get caught ok. Since ACHANNEL_FIRST is really ACHANNEL_HEIGHT, we simply use
2051          *              ACHANNEL_HEIGHT_HALF.
2052          */
2053         UI_view2d_region_to_view(v2d, evt->mval[0], evt->mval[1], &x, &y);
2054         
2055         if (ac.datatype == ANIMCONT_NLA) {
2056                 SpaceNla *snla = (SpaceNla *)ac.sl;
2057                 UI_view2d_listview_view_to_cell(v2d, NLACHANNEL_NAMEWIDTH, NLACHANNEL_STEP(snla), 0, (float)NLACHANNEL_HEIGHT_HALF(snla), x, y, NULL, &channel_index);
2058         }
2059         else {
2060                 UI_view2d_listview_view_to_cell(v2d, ACHANNEL_NAMEWIDTH, ACHANNEL_STEP, 0, (float)ACHANNEL_HEIGHT_HALF, x, y, NULL, &channel_index);
2061         }
2062         
2063         /* handle click */
2064         rename_anim_channels(&ac, channel_index);
2065         
2066         return OPERATOR_FINISHED;
2067 }
2068
2069 static void ANIM_OT_channels_rename(wmOperatorType *ot)
2070 {
2071         /* identifiers */
2072         ot->name = "Rename Channels";
2073         ot->idname = "ANIM_OT_channels_rename";
2074         ot->description = "Rename animation channel under mouse";
2075         
2076         /* api callbacks */
2077         ot->invoke = animchannels_rename_invoke;
2078         ot->poll = animedit_poll_channels_active;
2079 }
2080
2081 /* ******************** Mouse-Click Operator *********************** */
2082 /* Handle selection changes due to clicking on channels. Settings will get caught by UI code... */
2083
2084 static int mouse_anim_channels(bAnimContext *ac, float UNUSED(x), int channel_index, short selectmode)
2085 {
2086         ListBase anim_data = {NULL, NULL};
2087         bAnimListElem *ale;
2088         int filter;
2089         int notifierFlags = 0;
2090         
2091         /* get the channel that was clicked on */
2092         /* filter channels */
2093         filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_LIST_CHANNELS);
2094         ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
2095         
2096         /* get channel from index */
2097         ale = BLI_findlink(&anim_data, channel_index);
2098         if (ale == NULL) {
2099                 /* channel not found */
2100                 if (G.debug & G_DEBUG)
2101                         printf("Error: animation channel (index = %d) not found in mouse_anim_channels()\n", channel_index);
2102                 
2103                 BLI_freelistN(&anim_data);
2104                 return 0;
2105         }
2106
2107         /* selectmode -1 is a special case for ActionGroups only, which selects all of the channels underneath it only... */
2108         /* TODO: should this feature be extended to work with other channel types too? */
2109         if ((selectmode == -1) && (ale->type != ANIMTYPE_GROUP)) {
2110                 /* normal channels should not behave normally in this case */
2111                 BLI_freelistN(&anim_data);
2112                 return 0;
2113         }
2114
2115         /* action to take depends on what channel we've got */
2116         /* WARNING: must keep this in sync with the equivalent function in nla_channels.c */
2117         switch (ale->type) {
2118                 case ANIMTYPE_SCENE:
2119                 {
2120                         Scene *sce = (Scene *)ale->data;
2121                         AnimData *adt = sce->adt;
2122                         
2123                         /* set selection status */
2124                         if (selectmode == SELECT_INVERT) {
2125                                 /* swap select */
2126                                 sce->flag ^= SCE_DS_SELECTED;
2127                                 if (adt) adt->flag ^= ADT_UI_SELECTED;
2128                         }
2129                         else {
2130                                 sce->flag |= SCE_DS_SELECTED;
2131                                 if (adt) adt->flag |= ADT_UI_SELECTED;
2132                         }
2133                         
2134                         notifierFlags |= (ND_ANIMCHAN | NA_SELECTED);
2135                 }
2136                 break;
2137                 case ANIMTYPE_OBJECT:
2138                 {
2139                         bDopeSheet *ads = (bDopeSheet *)ac->data;
2140                         Scene *sce = (Scene *)ads->source;
2141                         Base *base = (Base *)ale->data;
2142                         Object *ob = base->object;
2143                         AnimData *adt = ob->adt;
2144                         
2145                         /* set selection status */
2146                         if (selectmode == SELECT_INVERT) {
2147                                 /* swap select */
2148                                 base->flag ^= SELECT;
2149                                 ob->flag = base->flag;
2150                                 
2151                                 if (adt) adt->flag ^= ADT_UI_SELECTED;
2152                         }
2153                         else {
2154                                 Base *b;
2155                                 
2156                                 /* deselect all */
2157                                 /* TODO: should this deselect all other types of channels too? */
2158                                 for (b = sce->base.first; b; b = b->next) {
2159                                         b->flag &= ~SELECT;
2160                                         b->object->flag = b->flag;
2161                                         if (b->object->adt) b->object->adt->flag &= ~(ADT_UI_SELECTED | ADT_UI_ACTIVE);
2162                                 }
2163                                 
2164                                 /* select object now */
2165                                 base->flag |= SELECT;
2166                                 ob->flag |= SELECT;
2167                                 if (adt) adt->flag |= ADT_UI_SELECTED;
2168                         }
2169                         
2170                         if ((adt) && (adt->flag & ADT_UI_SELECTED))
2171                                 adt->flag |= ADT_UI_ACTIVE;
2172                         
2173                         notifierFlags |= (ND_ANIMCHAN | NA_SELECTED);
2174                 }
2175                 break;
2176                 
2177                 case ANIMTYPE_FILLACTD: /* Action Expander */
2178                 case ANIMTYPE_DSMAT:    /* Datablock AnimData Expanders */
2179                 case ANIMTYPE_DSLAM:
2180                 case ANIMTYPE_DSCAM:
2181                 case ANIMTYPE_DSCUR:
2182                 case ANIMTYPE_DSSKEY:
2183                 case ANIMTYPE_DSWOR:
2184                 case ANIMTYPE_DSPART:
2185                 case ANIMTYPE_DSMBALL:
2186                 case ANIMTYPE_DSARM:
2187                 case ANIMTYPE_DSMESH:
2188                 case ANIMTYPE_DSNTREE:
2189                 case ANIMTYPE_DSTEX:
2190                 case ANIMTYPE_DSLAT:
2191                 case ANIMTYPE_DSSPK:
2192                 {
2193                         /* sanity checking... */
2194                         if (ale->adt) {
2195                                 /* select/deselect */
2196                                 if (selectmode == SELECT_INVERT) {
2197                                         /* inverse selection status of this AnimData block only */
2198                                         ale->adt->flag ^= ADT_UI_SELECTED;
2199                                 }
2200                                 else {
2201                                         /* select AnimData block by itself */
2202                                         ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, 0, ACHANNEL_SETFLAG_CLEAR);
2203                                         ale->adt->flag |= ADT_UI_SELECTED;
2204                                 }
2205                                 
2206                                 /* set active? */
2207                                 if ((ale->adt) && (ale->adt->flag & ADT_UI_SELECTED))
2208                                         ale->adt->flag |= ADT_UI_ACTIVE;
2209                         }
2210                         
2211                         notifierFlags |= (ND_ANIMCHAN | NA_SELECTED);
2212                 }       
2213                 break;
2214                 
2215                 case ANIMTYPE_GROUP: 
2216                 {
2217                         bActionGroup *agrp = (bActionGroup *)ale->data;
2218                         
2219                         /* select/deselect group */
2220                         if (selectmode == SELECT_INVERT) {
2221                                 /* inverse selection status of this group only */
2222                                 agrp->flag ^= AGRP_SELECTED;
2223                         }
2224                         else if (selectmode == -1) {
2225                                 /* select all in group (and deselect everthing else) */ 
2226                                 FCurve *fcu;
2227                                 
2228                                 /* deselect all other channels */
2229                                 ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, 0, ACHANNEL_SETFLAG_CLEAR);
2230                                 
2231                                 /* only select channels in group and group itself */
2232                                 for (fcu = agrp->channels.first; fcu && fcu->grp == agrp; fcu = fcu->next)
2233                                         fcu->flag |= FCURVE_SELECTED;
2234                                 agrp->flag |= AGRP_SELECTED;                                    
2235                         }
2236                         else {
2237                                 /* select group by itself */
2238                                 ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, 0, ACHANNEL_SETFLAG_CLEAR);
2239                                 agrp->flag |= AGRP_SELECTED;
2240                         }
2241                         
2242                         /* if group is selected now, make group the 'active' one in the visible list */
2243                         if (agrp->flag & AGRP_SELECTED)
2244                                 ANIM_set_active_channel(ac, ac->data, ac->datatype, filter, agrp, ANIMTYPE_GROUP);
2245                         else
2246                                 ANIM_set_active_channel(ac, ac->data, ac->datatype, filter, NULL, ANIMTYPE_GROUP);
2247                                 
2248                         notifierFlags |= (ND_ANIMCHAN | NA_SELECTED);
2249                 }
2250                 break;
2251                 case ANIMTYPE_FCURVE: 
2252                 {
2253                         FCurve *fcu = (FCurve *)ale->data;
2254                         
2255                         /* select/deselect */
2256                         if (selectmode == SELECT_INVERT) {
2257                                 /* inverse selection status of this F-Curve only */
2258                                 fcu->flag ^= FCURVE_SELECTED;
2259                         }
2260                         else {
2261                                 /* select F-Curve by itself */
2262                                 ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, 0, ACHANNEL_SETFLAG_CLEAR);
2263                                 fcu->flag |= FCURVE_SELECTED;
2264                         }
2265                         
2266                         /* if F-Curve is selected now, make F-Curve the 'active' one in the visible list */
2267                         if (fcu->flag & FCURVE_SELECTED)
2268                                 ANIM_set_active_channel(ac, ac->data, ac->datatype, filter, fcu, ANIMTYPE_FCURVE);
2269                                 
2270                         notifierFlags |= (ND_ANIMCHAN | NA_SELECTED);
2271                 }
2272                 break;
2273                 case ANIMTYPE_SHAPEKEY: 
2274                 {
2275                         KeyBlock *kb = (KeyBlock *)ale->data;
2276                         
2277                         /* select/deselect */
2278                         if (selectmode == SELECT_INVERT) {
2279                                 /* inverse selection status of this ShapeKey only */
2280                                 kb->flag ^= KEYBLOCK_SEL;
2281                         }
2282                         else {
2283                                 /* select ShapeKey by itself */
2284                                 ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, 0, ACHANNEL_SETFLAG_CLEAR);
2285                                 kb->flag |= KEYBLOCK_SEL;
2286                         }
2287                                 
2288                         notifierFlags |= (ND_ANIMCHAN | NA_SELECTED);
2289                 }
2290                 break;
2291                 case ANIMTYPE_GPDATABLOCK:
2292                 {
2293                         bGPdata *gpd = (bGPdata *)ale->data;
2294                         
2295                         /* toggle expand 
2296                          *      - although the triangle widget already allows this, the whole channel can also be used for this purpose
2297                          */
2298                         gpd->flag ^= GP_DATA_EXPAND;
2299                         
2300                         notifierFlags |= (ND_ANIMCHAN | NA_EDITED);
2301                 }
2302                 break;
2303                 case ANIMTYPE_GPLAYER:
2304                 {
2305                         bGPDlayer *gpl = (bGPDlayer *)ale->data;
2306                         
2307                         /* select/deselect */
2308                         if (selectmode == SELECT_INVERT) {
2309                                 /* invert selection status of this layer only */
2310                                 gpl->flag ^= GP_LAYER_SELECT;
2311                         }
2312                         else {  
2313                                 /* select layer by itself */
2314                                 ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, 0, ACHANNEL_SETFLAG_CLEAR);
2315                                 gpl->flag |= GP_LAYER_SELECT;
2316                         }
2317                         
2318                         notifierFlags |= (ND_ANIMCHAN | NA_EDITED);
2319                 }
2320                 break;
2321                 case ANIMTYPE_MASKDATABLOCK:
2322                 {
2323                         Mask *mask = (Mask *)ale->data;
2324                         
2325                         /* toggle expand
2326                          *      - although the triangle widget already allows this, the whole channel can also be used for this purpose
2327                          */
2328                         mask->flag ^= MASK_ANIMF_EXPAND;
2329                         
2330                         notifierFlags |= (ND_ANIMCHAN | NA_EDITED);
2331                 }
2332                 break;
2333                 case ANIMTYPE_MASKLAYER:
2334                 {
2335                         MaskLayer *masklay = (MaskLayer *)ale->data;
2336                         
2337                         /* select/deselect */
2338                         if (selectmode == SELECT_INVERT) {
2339                                 /* invert selection status of this layer only */
2340                                 masklay->flag ^= MASK_LAYERFLAG_SELECT;
2341                         }
2342                         else {
2343                                 /* select layer by itself */
2344                                 ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, 0, ACHANNEL_SETFLAG_CLEAR);
2345                                 masklay->flag |= MASK_LAYERFLAG_SELECT;
2346                         }
2347                         
2348                         notifierFlags |= (ND_ANIMCHAN | NA_EDITED);
2349                 }
2350                 break;
2351                 default:
2352                         if (G.debug & G_DEBUG)
2353                                 printf("Error: Invalid channel type in mouse_anim_channels()\n");
2354         }
2355         
2356         /* free channels */
2357         BLI_freelistN(&anim_data);
2358         
2359         /* return notifier flags */
2360         return notifierFlags;
2361 }
2362
2363 /* ------------------- */
2364
2365 /* handle clicking */
2366 static int animchannels_mouseclick_invoke(bContext *C, wmOperator *op, wmEvent *event)
2367 {
2368         bAnimContext ac;
2369         ARegion *ar;
2370         View2D *v2d;
2371         int channel_index;
2372         int notifierFlags = 0;
2373         short selectmode;
2374         float x, y;
2375         
2376         
2377         /* get editor data */
2378         if (ANIM_animdata_get_context(C, &ac) == 0)
2379                 return OPERATOR_CANCELLED;
2380                 
2381         /* get useful pointers from animation context data */
2382         ar = ac.ar;
2383         v2d = &ar->v2d;
2384         
2385         /* select mode is either replace (deselect all, then add) or add/extend */
2386         if (RNA_boolean_get(op->ptr, "extend"))
2387                 selectmode = SELECT_INVERT;
2388         else if (RNA_boolean_get(op->ptr, "children_only"))
2389                 selectmode = -1;  /* this is a bit of a special case for ActionGroups only... should it be removed or extended to all instead? */
2390         else
2391                 selectmode = SELECT_REPLACE;
2392         
2393         /* figure out which channel user clicked in 
2394          * Note: although channels technically start at (y = ACHANNEL_FIRST), we need to adjust by half a channel's height
2395          *              so that the tops of channels get caught ok. Since ACHANNEL_FIRST is really ACHANNEL_HEIGHT, we simply use
2396          *              ACHANNEL_HEIGHT_HALF.
2397          */
2398         UI_view2d_region_to_view(v2d, event->mval[0], event->mval[1], &x, &y);
2399         UI_view2d_listview_view_to_cell(v2d, ACHANNEL_NAMEWIDTH, ACHANNEL_STEP, 0, (float)ACHANNEL_HEIGHT_HALF, x, y, NULL, &channel_index);
2400         
2401         /* handle mouse-click in the relevant channel then */
2402         notifierFlags = mouse_anim_channels(&ac, x, channel_index, selectmode);
2403         
2404         /* set notifier that things have changed */
2405         WM_event_add_notifier(C, NC_ANIMATION | notifierFlags, NULL);
2406         
2407         return OPERATOR_FINISHED;
2408 }
2409  
2410 static void ANIM_OT_channels_click(wmOperatorType *ot)
2411 {
2412         PropertyRNA *prop;
2413         
2414         /* identifiers */
2415         ot->name = "Mouse Click on Channels";
2416         ot->idname = "ANIM_OT_channels_click";
2417         ot->description = "Handle mouse-clicks over animation channels";
2418         
2419         /* api callbacks */
2420         ot->invoke = animchannels_mouseclick_invoke;
2421         ot->poll = animedit_poll_channels_active;
2422         
2423         /* flags */
2424         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2425         
2426         /* properties */
2427         /* NOTE: don't save settings, otherwise, can end up with some weird behaviour (sticky extend) */
2428         prop = RNA_def_boolean(ot->srna, "extend", 0, "Extend Select", ""); // SHIFTKEY
2429         RNA_def_property_flag(prop, PROP_SKIP_SAVE);
2430         
2431         prop = RNA_def_boolean(ot->srna, "children_only", 0, "Select Children Only", ""); // CTRLKEY|SHIFTKEY
2432         RNA_def_property_flag(prop, PROP_SKIP_SAVE);
2433 }
2434
2435 /* ************************************************************************** */
2436 /* Operator Registration */
2437
2438 void ED_operatortypes_animchannels(void)
2439 {
2440         WM_operatortype_append(ANIM_OT_channels_select_all_toggle);
2441         WM_operatortype_append(ANIM_OT_channels_select_border);
2442         
2443         WM_operatortype_append(ANIM_OT_channels_click);
2444         WM_operatortype_append(ANIM_OT_channels_rename);
2445         
2446         WM_operatortype_append(ANIM_OT_channels_setting_enable);
2447         WM_operatortype_append(ANIM_OT_channels_setting_disable);
2448         WM_operatortype_append(ANIM_OT_channels_setting_toggle);
2449
2450         WM_operatortype_append(ANIM_OT_channels_delete);
2451
2452         /* XXX does this need to be a separate operator? */
2453         WM_operatortype_append(ANIM_OT_channels_editable_toggle);
2454         
2455         WM_operatortype_append(ANIM_OT_channels_move);
2456         
2457         WM_operatortype_append(ANIM_OT_channels_expand);
2458         WM_operatortype_append(ANIM_OT_channels_collapse);
2459         
2460         WM_operatortype_append(ANIM_OT_channels_visibility_toggle);
2461         WM_operatortype_append(ANIM_OT_channels_visibility_set);
2462         
2463         WM_operatortype_append(ANIM_OT_channels_fcurves_enable);
2464 }
2465
2466 // TODO: check on a poll callback for this, to get hotkeys into menus
2467 void ED_keymap_animchannels(wmKeyConfig *keyconf)
2468 {
2469         wmKeyMap *keymap = WM_keymap_find(keyconf, "Animation Channels", 0, 0);
2470         wmKeyMapItem *kmi;
2471
2472         /* selection */
2473         /* click-select */
2474         /* XXX for now, only leftmouse.... */
2475         WM_keymap_add_item(keymap, "ANIM_OT_channels_click", LEFTMOUSE, KM_PRESS, 0, 0);
2476         RNA_boolean_set(WM_keymap_add_item(keymap, "ANIM_OT_channels_click", LEFTMOUSE, KM_PRESS, KM_SHIFT, 0)->ptr, "extend", TRUE);
2477         RNA_boolean_set(WM_keymap_add_item(keymap, "ANIM_OT_channels_click", LEFTMOUSE, KM_PRESS, KM_CTRL | KM_SHIFT, 0)->ptr, "children_only", TRUE);
2478
2479         /* rename */
2480         WM_keymap_add_item(keymap, "ANIM_OT_channels_rename", LEFTMOUSE, KM_PRESS, KM_CTRL, 0);
2481         
2482         /* deselect all */
2483         WM_keymap_add_item(keymap, "ANIM_OT_channels_select_all_toggle", AKEY, KM_PRESS, 0, 0);
2484         RNA_boolean_set(WM_keymap_add_item(keymap, "ANIM_OT_channels_select_all_toggle", IKEY, KM_PRESS, KM_CTRL, 0)->ptr, "invert", TRUE);
2485         
2486         /* borderselect */
2487         WM_keymap_add_item(keymap, "ANIM_OT_channels_select_border", BKEY, KM_PRESS, 0, 0);
2488         WM_keymap_add_item(keymap, "ANIM_OT_channels_select_border", EVT_TWEAK_L, KM_ANY, 0, 0);
2489         
2490         /* delete */
2491         WM_keymap_add_item(keymap, "ANIM_OT_channels_delete", XKEY, KM_PRESS, 0, 0);
2492         WM_keymap_add_item(keymap, "ANIM_OT_channels_delete", DELKEY, KM_PRESS, 0, 0);
2493         
2494         /* settings */
2495         WM_keymap_add_item(keymap, "ANIM_OT_channels_setting_toggle", WKEY, KM_PRESS, KM_SHIFT, 0);
2496         WM_keymap_add_item(keymap, "ANIM_OT_channels_setting_enable", WKEY, KM_PRESS, KM_CTRL | KM_SHIFT, 0);
2497         WM_keymap_add_item(keymap, "ANIM_OT_channels_setting_disable", WKEY, KM_PRESS, KM_ALT, 0);
2498         
2499         /* settings - specialized hotkeys */
2500         WM_keymap_add_item(keymap, "ANIM_OT_channels_editable_toggle", TABKEY, KM_PRESS, 0, 0);
2501         
2502         /* expand/collapse */
2503         WM_keymap_add_item(keymap, "ANIM_OT_channels_expand", PADPLUSKEY, KM_PRESS, 0, 0);
2504         WM_keymap_add_item(keymap, "ANIM_OT_channels_collapse", PADMINUS, KM_PRESS, 0, 0);
2505         
2506         kmi = WM_keymap_add_item(keymap, "ANIM_OT_channels_expand", PADPLUSKEY, KM_PRESS, KM_CTRL, 0);
2507         RNA_boolean_set(kmi->ptr, "all", FALSE);
2508         kmi = WM_keymap_add_item(keymap, "ANIM_OT_channels_collapse", PADMINUS, KM_PRESS, KM_CTRL, 0);
2509         RNA_boolean_set(kmi->ptr, "all", FALSE);
2510
2511         /* rearranging */
2512         RNA_enum_set(WM_keymap_add_item(keymap, "ANIM_OT_channels_move", PAGEUPKEY, KM_PRESS, 0, 0)->ptr, "direction", REARRANGE_ANIMCHAN_UP);
2513         RNA_enum_set(WM_keymap_add_item(keymap, "ANIM_OT_channels_move", PAGEDOWNKEY, KM_PRESS, 0, 0)->ptr, "direction", REARRANGE_ANIMCHAN_DOWN);
2514         RNA_enum_set(WM_keymap_add_item(keymap, "ANIM_OT_channels_move", PAGEUPKEY, KM_PRESS, KM_SHIFT, 0)->ptr, "direction", REARRANGE_ANIMCHAN_TOP);
2515         RNA_enum_set(WM_keymap_add_item(keymap, "ANIM_OT_channels_move", PAGEDOWNKEY, KM_PRESS, KM_SHIFT, 0)->ptr, "direction", REARRANGE_ANIMCHAN_BOTTOM);
2516         
2517         /* Graph Editor only */
2518         WM_keymap_add_item(keymap, "ANIM_OT_channels_visibility_set", VKEY, KM_PRESS, 0, 0);
2519         WM_keymap_add_item(keymap, "ANIM_OT_channels_visibility_toggle", VKEY, KM_PRESS, KM_SHIFT, 0);
2520 }
2521
2522 /* ************************************************************************** */