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