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