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