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