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