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