Blender V2.61 - r43446

anim_channels_edit.c

Go to the documentation of this file.
00001 /*
00002  * ***** BEGIN GPL LICENSE BLOCK *****
00003  *
00004  * This program is free software; you can redistribute it and/or
00005  * modify it under the terms of the GNU General Public License
00006  * as published by the Free Software Foundation; either version 2
00007  * of the License, or (at your option) any later version.
00008  *
00009  * This program is distributed in the hope that it will be useful,
00010  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  * GNU General Public License for more details.
00013  *
00014  * You should have received a copy of the GNU General Public License
00015  * along with this program; if not, write to the Free Software Foundation,
00016  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00017  *
00018  * The Original Code is Copyright (C) 2009 Blender Foundation, Joshua Leung
00019  * All rights reserved.
00020  *
00021  * Contributor(s): Joshua Leung
00022  *
00023  * ***** END GPL LICENSE BLOCK *****
00024  */
00025 
00031 #include <stdio.h>
00032 #include <stdlib.h>
00033 #include <string.h> 
00034 
00035 #include "MEM_guardedalloc.h"
00036 
00037 #include "BLI_blenlib.h"
00038 #include "BLI_utildefines.h"
00039 #include "BKE_library.h"
00040 
00041 #include "DNA_anim_types.h"
00042 #include "DNA_object_types.h"
00043 #include "DNA_scene_types.h"
00044 #include "DNA_key_types.h"
00045 #include "DNA_gpencil_types.h"
00046 
00047 #include "RNA_access.h"
00048 #include "RNA_define.h"
00049 
00050 #include "BKE_action.h"
00051 #include "BKE_fcurve.h"
00052 #include "BKE_gpencil.h"
00053 #include "BKE_context.h"
00054 #include "BKE_global.h"
00055 
00056 #include "UI_view2d.h"
00057 
00058 #include "ED_anim_api.h"
00059 #include "ED_keyframes_edit.h" // XXX move the select modes out of there!
00060 #include "ED_screen.h"
00061 
00062 #include "WM_api.h"
00063 #include "WM_types.h"
00064 
00065 /* ************************************************************************** */
00066 /* CHANNELS API - Exposed API */
00067 
00068 /* -------------------------- Selection ------------------------------------- */
00069 
00070 /* Set the given animation-channel as the active one for the active context */
00071 // TODO: extend for animdata types...
00072 void ANIM_set_active_channel (bAnimContext *ac, void *data, short datatype, int filter, void *channel_data, short channel_type)
00073 {
00074     ListBase anim_data = {NULL, NULL};
00075     bAnimListElem *ale;
00076     
00077     /* try to build list of filtered items */
00078     ANIM_animdata_filter(ac, &anim_data, filter, data, datatype);
00079     if (anim_data.first == NULL)
00080         return;
00081         
00082     /* only clear the 'active' flag for the channels of the same type */
00083     for (ale= anim_data.first; ale; ale= ale->next) {
00084         /* skip if types don't match */
00085         if (channel_type != ale->type)
00086             continue;
00087         
00088         /* flag to set depends on type */
00089         switch (ale->type) {
00090             case ANIMTYPE_GROUP:
00091             {
00092                 bActionGroup *agrp= (bActionGroup *)ale->data;
00093                 
00094                 ACHANNEL_SET_FLAG(agrp, ACHANNEL_SETFLAG_CLEAR, AGRP_ACTIVE);
00095             }
00096                 break;
00097             case ANIMTYPE_FCURVE:
00098             {
00099                 FCurve *fcu= (FCurve *)ale->data;
00100                 
00101                 ACHANNEL_SET_FLAG(fcu, ACHANNEL_SETFLAG_CLEAR, FCURVE_ACTIVE);
00102             }
00103                 break;
00104             case ANIMTYPE_NLATRACK:
00105             {
00106                 NlaTrack *nlt= (NlaTrack *)ale->data;
00107                 
00108                 ACHANNEL_SET_FLAG(nlt, ACHANNEL_SETFLAG_CLEAR, NLATRACK_ACTIVE);
00109             }
00110                 break;
00111             
00112             case ANIMTYPE_FILLACTD: /* Action Expander */
00113             case ANIMTYPE_DSMAT:    /* Datablock AnimData Expanders */
00114             case ANIMTYPE_DSLAM:
00115             case ANIMTYPE_DSCAM:
00116             case ANIMTYPE_DSCUR:
00117             case ANIMTYPE_DSSKEY:
00118             case ANIMTYPE_DSWOR:
00119             case ANIMTYPE_DSPART:
00120             case ANIMTYPE_DSMBALL:
00121             case ANIMTYPE_DSARM:
00122             case ANIMTYPE_DSMESH:
00123             case ANIMTYPE_DSTEX:
00124             case ANIMTYPE_DSLAT:
00125             case ANIMTYPE_DSSPK:
00126             {
00127                 /* need to verify that this data is valid for now */
00128                 if (ale->adt) {
00129                     ACHANNEL_SET_FLAG(ale->adt, ACHANNEL_SETFLAG_CLEAR, ADT_UI_ACTIVE);
00130                 }
00131             }
00132                 break;
00133         }
00134     }
00135     
00136     /* set active flag */
00137     if (channel_data) {
00138         switch (channel_type) {
00139             case ANIMTYPE_GROUP:
00140             {
00141                 bActionGroup *agrp= (bActionGroup *)channel_data;
00142                 agrp->flag |= AGRP_ACTIVE;
00143             }
00144                 break;
00145             case ANIMTYPE_FCURVE:
00146             {
00147                 FCurve *fcu= (FCurve *)channel_data;
00148                 fcu->flag |= FCURVE_ACTIVE;
00149             }
00150                 break;
00151             case ANIMTYPE_NLATRACK:
00152             {
00153                 NlaTrack *nlt= (NlaTrack *)channel_data;
00154                 nlt->flag |= NLATRACK_ACTIVE;
00155             }
00156                 break;
00157                 
00158             case ANIMTYPE_FILLACTD: /* Action Expander */
00159             case ANIMTYPE_DSMAT:    /* Datablock AnimData Expanders */
00160             case ANIMTYPE_DSLAM:
00161             case ANIMTYPE_DSCAM:
00162             case ANIMTYPE_DSCUR:
00163             case ANIMTYPE_DSSKEY:
00164             case ANIMTYPE_DSWOR:
00165             case ANIMTYPE_DSPART:
00166             case ANIMTYPE_DSMBALL:
00167             case ANIMTYPE_DSARM:
00168             case ANIMTYPE_DSMESH:
00169             case ANIMTYPE_DSLAT:
00170             case ANIMTYPE_DSSPK:
00171             {
00172                 /* need to verify that this data is valid for now */
00173                 if (ale && ale->adt) {
00174                     ale->adt->flag |= ADT_UI_ACTIVE;
00175                 }
00176             }
00177                 break;
00178         }
00179     }
00180     
00181     /* clean up */
00182     BLI_freelistN(&anim_data);
00183 }
00184 
00185 /* Deselect all animation channels 
00186  *  - data: pointer to datatype, as contained in bAnimContext
00187  *  - datatype: the type of data that 'data' represents (eAnimCont_Types)
00188  *  - test: check if deselecting instead of selecting
00189  *  - sel: eAnimChannels_SetFlag;
00190  */
00191 void ANIM_deselect_anim_channels (bAnimContext *ac, void *data, short datatype, short test, short sel)
00192 {
00193     ListBase anim_data = {NULL, NULL};
00194     bAnimListElem *ale;
00195     int filter;
00196     
00197     /* filter data */
00198     /* NOTE: no list visible, otherwise, we get dangling */
00199     filter= ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_CHANNELS;
00200     ANIM_animdata_filter(ac, &anim_data, filter, data, datatype);
00201     
00202     /* See if we should be selecting or deselecting */
00203     if (test) {
00204         for (ale= anim_data.first; ale; ale= ale->next) {
00205             if (sel == 0) 
00206                 break;
00207             
00208             switch (ale->type) {
00209                 case ANIMTYPE_SCENE:
00210                     if (ale->flag & SCE_DS_SELECTED)
00211                         sel= ACHANNEL_SETFLAG_CLEAR;
00212                     break;
00213                 case ANIMTYPE_OBJECT:
00214                 #if 0   /* for now, do not take object selection into account, since it gets too annoying */
00215                     if (ale->flag & SELECT)
00216                         sel= ACHANNEL_SETFLAG_CLEAR;
00217                 #endif
00218                     break;
00219                 case ANIMTYPE_GROUP:
00220                     if (ale->flag & AGRP_SELECTED)
00221                         sel= ACHANNEL_SETFLAG_CLEAR;
00222                     break;
00223                 case ANIMTYPE_FCURVE:
00224                     if (ale->flag & FCURVE_SELECTED)
00225                         sel= ACHANNEL_SETFLAG_CLEAR;
00226                     break;
00227                 case ANIMTYPE_SHAPEKEY:
00228                     if (ale->flag & KEYBLOCK_SEL)
00229                         sel= ACHANNEL_SETFLAG_CLEAR;
00230                     break;
00231                 case ANIMTYPE_NLATRACK:
00232                     if (ale->flag & NLATRACK_SELECTED)
00233                         sel= ACHANNEL_SETFLAG_CLEAR;
00234                     break;
00235                     
00236                 case ANIMTYPE_FILLACTD: /* Action Expander */
00237                 case ANIMTYPE_DSMAT:    /* Datablock AnimData Expanders */
00238                 case ANIMTYPE_DSLAM:
00239                 case ANIMTYPE_DSCAM:
00240                 case ANIMTYPE_DSCUR:
00241                 case ANIMTYPE_DSSKEY:
00242                 case ANIMTYPE_DSWOR:
00243                 case ANIMTYPE_DSPART:
00244                 case ANIMTYPE_DSMBALL:
00245                 case ANIMTYPE_DSARM:
00246                 case ANIMTYPE_DSMESH:
00247                 case ANIMTYPE_DSNTREE:
00248                 case ANIMTYPE_DSTEX:
00249                 case ANIMTYPE_DSLAT:
00250                 case ANIMTYPE_DSSPK:
00251                 {
00252                     if ((ale->adt) && (ale->adt->flag & ADT_UI_SELECTED))
00253                         sel= ACHANNEL_SETFLAG_CLEAR;
00254                 }
00255                     break;
00256                     
00257                 case ANIMTYPE_GPLAYER:
00258                     if (ale->flag & GP_LAYER_SELECT)
00259                         sel= ACHANNEL_SETFLAG_CLEAR;
00260                     break;
00261             }
00262         }
00263     }
00264         
00265     /* Now set the flags */
00266     for (ale= anim_data.first; ale; ale= ale->next) {
00267         switch (ale->type) {
00268             case ANIMTYPE_SCENE:
00269             {
00270                 Scene *scene= (Scene *)ale->data;
00271                 
00272                 ACHANNEL_SET_FLAG(scene, sel, SCE_DS_SELECTED);
00273                 
00274                 if (scene->adt) {
00275                     ACHANNEL_SET_FLAG(scene, sel, ADT_UI_SELECTED);
00276                 }
00277             }
00278                 break;
00279             case ANIMTYPE_OBJECT:
00280             #if 0   /* for now, do not take object selection into account, since it gets too annoying */
00281             {
00282                 Base *base= (Base *)ale->data;
00283                 Object *ob= base->object;
00284                 
00285                 ACHANNEL_SET_FLAG(base, sel, SELECT);
00286                 ACHANNEL_SET_FLAG(ob, sel, SELECT);
00287                 
00288                 if (ob->adt) {
00289                     ACHANNEL_SET_FLAG(ob, sel, ADT_UI_SELECTED);
00290                 }
00291             }
00292             #endif
00293                 break;
00294             case ANIMTYPE_GROUP:
00295             {
00296                 bActionGroup *agrp= (bActionGroup *)ale->data;
00297                 
00298                 ACHANNEL_SET_FLAG(agrp, sel, AGRP_SELECTED);
00299                 agrp->flag &= ~AGRP_ACTIVE;
00300             }
00301                 break;
00302             case ANIMTYPE_FCURVE:
00303             {
00304                 FCurve *fcu= (FCurve *)ale->data;
00305                 
00306                 ACHANNEL_SET_FLAG(fcu, sel, FCURVE_SELECTED);
00307                 fcu->flag &= ~FCURVE_ACTIVE;
00308             }
00309                 break;
00310             case ANIMTYPE_SHAPEKEY:
00311             {
00312                 KeyBlock *kb= (KeyBlock *)ale->data;
00313                 
00314                 ACHANNEL_SET_FLAG(kb, sel, KEYBLOCK_SEL);
00315             }
00316                 break;
00317             case ANIMTYPE_NLATRACK:
00318             {
00319                 NlaTrack *nlt= (NlaTrack *)ale->data;
00320                 
00321                 ACHANNEL_SET_FLAG(nlt, sel, NLATRACK_SELECTED);
00322                 nlt->flag &= ~NLATRACK_ACTIVE;
00323             }
00324                 break;
00325                 
00326             case ANIMTYPE_FILLACTD: /* Action Expander */
00327             case ANIMTYPE_DSMAT:    /* Datablock AnimData Expanders */
00328             case ANIMTYPE_DSLAM:
00329             case ANIMTYPE_DSCAM:
00330             case ANIMTYPE_DSCUR:
00331             case ANIMTYPE_DSSKEY:
00332             case ANIMTYPE_DSWOR:
00333             case ANIMTYPE_DSPART:
00334             case ANIMTYPE_DSMBALL:
00335             case ANIMTYPE_DSARM:
00336             case ANIMTYPE_DSMESH:
00337             case ANIMTYPE_DSNTREE:
00338             case ANIMTYPE_DSTEX:
00339             case ANIMTYPE_DSLAT:
00340             case ANIMTYPE_DSSPK:
00341             {
00342                 /* need to verify that this data is valid for now */
00343                 if (ale->adt) {
00344                     ACHANNEL_SET_FLAG(ale->adt, sel, ADT_UI_SELECTED);
00345                     ale->adt->flag &= ~ADT_UI_ACTIVE;
00346                 }
00347             }
00348                 break;
00349                 
00350             case ANIMTYPE_GPLAYER:
00351             {
00352                 bGPDlayer *gpl = (bGPDlayer *)ale->data;
00353                 
00354                 ACHANNEL_SET_FLAG(gpl, sel, GP_LAYER_SELECT);
00355             }
00356                 break;
00357         }
00358     }
00359     
00360     /* Cleanup */
00361     BLI_freelistN(&anim_data);
00362 }
00363 
00364 /* ---------------------------- Graph Editor ------------------------------------- */
00365 
00366 /* Flush visibility (for Graph Editor) changes up/down hierarchy for changes in the given setting 
00367  *  - anim_data: list of the all the anim channels that can be chosen
00368  *      -> filtered using ANIMFILTER_CHANNELS only, since if we took VISIBLE too,
00369  *        then the channels under closed expanders get ignored...
00370  *  - ale_setting: the anim channel (not in the anim_data list directly, though occuring there)
00371  *      with the new state of the setting that we want flushed up/down the hierarchy 
00372  *  - setting: type of setting to set
00373  *  - on: whether the visibility setting has been enabled or disabled 
00374  */
00375 void ANIM_flush_setting_anim_channels (bAnimContext *ac, ListBase *anim_data, bAnimListElem *ale_setting, int setting, short on)
00376 {
00377     bAnimListElem *ale, *match=NULL;
00378     int prevLevel=0, matchLevel=0;
00379     
00380     /* sanity check */
00381     if (ELEM(NULL, anim_data, anim_data->first))
00382         return;
00383     
00384     /* find the channel that got changed */
00385     for (ale= anim_data->first; ale; ale= ale->next) {
00386         /* compare data, and type as main way of identifying the channel */
00387         if ((ale->data == ale_setting->data) && (ale->type == ale_setting->type)) {
00388             /* we also have to check the ID, this is assigned to, since a block may have multiple users */
00389             // TODO: is the owner-data more revealing?
00390             if (ale->id == ale_setting->id) {
00391                 match= ale;
00392                 break;
00393             }
00394         }
00395     }
00396     if (match == NULL) {
00397         printf("ERROR: no channel matching the one changed was found \n");
00398         return;
00399     }
00400     else {
00401         bAnimChannelType *acf= ANIM_channel_get_typeinfo(ale_setting);
00402         
00403         if (acf == NULL) {
00404             printf("ERROR: no channel info for the changed channel \n");
00405             return;
00406         }
00407         
00408         /* get the level of the channel that was affected
00409          *   - we define the level as simply being the offset for the start of the channel
00410          */
00411         matchLevel= (acf->get_offset)? acf->get_offset(ac, ale_setting) : 0;
00412         prevLevel= matchLevel;
00413     }
00414     
00415     /* flush up? 
00416      *
00417      * For Visibility:
00418      *  - only flush up if the current state is now enabled (positive 'on' state is default) 
00419      *    (otherwise, it's too much work to force the parents to be inactive too)
00420      *
00421      * For everything else:
00422      *  - only flush up if the current state is now disabled (negative 'off' state is default)
00423      *    (otherwise, it's too much work to force the parents to be active too)
00424      */
00425     if ( ((setting == ACHANNEL_SETTING_VISIBLE) && on) ||
00426          ((setting != ACHANNEL_SETTING_VISIBLE) && on==0) )
00427     {
00428         /* go backwards in the list, until the highest-ranking element (by indention has been covered) */
00429         for (ale= match->prev; ale; ale= ale->prev) {
00430             bAnimChannelType *acf= ANIM_channel_get_typeinfo(ale);
00431             int level;
00432             
00433             /* if no channel info was found, skip, since this type might not have any useful info */
00434             if (acf == NULL)
00435                 continue;
00436             
00437             /* get the level of the current channel traversed 
00438              *   - we define the level as simply being the offset for the start of the channel
00439              */
00440             level= (acf->get_offset)? acf->get_offset(ac, ale) : 0;
00441             
00442             /* if the level is 'less than' (i.e. more important) the level we're matching
00443              * but also 'less than' the level just tried (i.e. only the 1st group above grouped F-Curves, 
00444              * when toggling visibility of F-Curves, gets flushed, which should happen if we don't let prevLevel
00445              * get updated below once the first 1st group is found)...
00446              */
00447             if (level < prevLevel) {
00448                 /* flush the new status... */
00449                 ANIM_channel_setting_set(ac, ale, setting, on);
00450                 
00451                 /* store this level as the 'old' level now */
00452                 prevLevel= level;
00453             }   
00454             /* if the level is 'greater than' (i.e. less important) than the previous level... */
00455             else if (level > prevLevel) {
00456                 /* if previous level was a base-level (i.e. 0 offset / root of one hierarchy),
00457                  * stop here
00458                  */
00459                 if (prevLevel == 0)
00460                     break;
00461                 /* otherwise, this level weaves into another sibling hierarchy to the previous one just
00462                  * finished, so skip until we get to the parent of this level 
00463                  */
00464                 else
00465                     continue;
00466             }
00467         }
00468     }
00469     
00470     /* flush down (always) */
00471     {
00472         /* go forwards in the list, until the lowest-ranking element (by indention has been covered) */
00473         for (ale= match->next; ale; ale= ale->next) {
00474             bAnimChannelType *acf= ANIM_channel_get_typeinfo(ale);
00475             int level;
00476             
00477             /* if no channel info was found, skip, since this type might not have any useful info */
00478             if (acf == NULL)
00479                 continue;
00480             
00481             /* get the level of the current channel traversed 
00482              *   - we define the level as simply being the offset for the start of the channel
00483              */
00484             level= (acf->get_offset)? acf->get_offset(ac, ale) : 0;
00485             
00486             /* if the level is 'greater than' (i.e. less important) the channel that was changed, 
00487              * flush the new status...
00488              */
00489             if (level > matchLevel)
00490                 ANIM_channel_setting_set(ac, ale, setting, on);
00491             /* however, if the level is 'less than or equal to' the channel that was changed,
00492              * (i.e. the current channel is as important if not more important than the changed channel)
00493              * then we should stop, since we've found the last one of the children we should flush
00494              */
00495             else
00496                 break;
00497             
00498             /* store this level as the 'old' level now */
00499             // prevLevel= level; // XXX: prevLevel is unused
00500         }
00501     }
00502 }
00503 
00504 /* -------------------------- F-Curves ------------------------------------- */
00505 
00506 /* Delete the given F-Curve from its AnimData block */
00507 void ANIM_fcurve_delete_from_animdata (bAnimContext *ac, AnimData *adt, FCurve *fcu)
00508 {
00509     /* - if no AnimData, we've got nowhere to remove the F-Curve from 
00510      *  (this doesn't guarantee that the F-Curve is in there, but at least we tried
00511      * - if no F-Curve, there is nothing to remove
00512      */
00513     if (ELEM(NULL, adt, fcu))
00514         return;
00515         
00516     /* remove from whatever list it came from
00517      *  - Action Group
00518      *  - Action
00519      *  - Drivers
00520      *  - TODO... some others?
00521      */
00522     if ((ac) && (ac->datatype == ANIMCONT_DRIVERS)) {
00523         /* driver F-Curve */
00524         BLI_remlink(&adt->drivers, fcu);
00525     }
00526     else if (adt->action) {
00527         /* remove from group or action, whichever one "owns" the F-Curve */
00528         if (fcu->grp)
00529             action_groups_remove_channel(adt->action, fcu);
00530         else
00531             BLI_remlink(&adt->action->curves, fcu);
00532             
00533         /* if action has no more F-Curves as a result of this, unlink it from
00534          * AnimData if it did not come from a NLA Strip being tweaked.
00535          *
00536          * This is done so that we don't have dangling Object+Action entries in
00537          * channel list that are empty, and linger around long after the data they
00538          * are for has disappeared (and probably won't come back).
00539          */
00540             // XXX: does everybody always want this?
00541             /* XXX: there's a problem where many actions could build up in the file if multiple
00542              * full add/delete cycles are performed on the same objects, but assume that this is rare
00543              */
00544         if ((adt->action->curves.first == NULL) && (adt->flag & ADT_NLA_EDIT_ON)==0)
00545         {
00546             id_us_min(&adt->action->id);
00547             adt->action = NULL;
00548         }
00549     }
00550         
00551     /* free the F-Curve itself */
00552     free_fcurve(fcu);
00553 }
00554 
00555 /* ************************************************************************** */
00556 /* OPERATORS */
00557 
00558 /* ****************** Operator Utilities ********************************** */
00559 
00560 /* poll callback for being in an Animation Editor channels list region */
00561 static int animedit_poll_channels_active (bContext *C)
00562 {
00563     ScrArea *sa= CTX_wm_area(C);
00564     
00565     /* channels region test */
00566     // TODO: could enhance with actually testing if channels region?
00567     if (ELEM(NULL, sa, CTX_wm_region(C)))
00568         return 0;
00569     /* animation editor test */
00570     if (ELEM3(sa->spacetype, SPACE_ACTION, SPACE_IPO, SPACE_NLA) == 0)
00571         return 0;
00572         
00573     return 1;
00574 }
00575 
00576 /* poll callback for Animation Editor channels list region + not in NLA-tweakmode for NLA */
00577 static int animedit_poll_channels_nla_tweakmode_off (bContext *C)
00578 {
00579     ScrArea *sa= CTX_wm_area(C);
00580     Scene *scene = CTX_data_scene(C);
00581     
00582     /* channels region test */
00583     // TODO: could enhance with actually testing if channels region?
00584     if (ELEM(NULL, sa, CTX_wm_region(C)))
00585         return 0;
00586     /* animation editor test */
00587     if (ELEM3(sa->spacetype, SPACE_ACTION, SPACE_IPO, SPACE_NLA) == 0)
00588         return 0;
00589     
00590     /* NLA TweakMode test */    
00591     if (sa->spacetype == SPACE_NLA) {
00592         if ((scene == NULL) || (scene->flag & SCE_NLA_EDIT_ON))
00593             return 0;
00594     }
00595         
00596     return 1;
00597 }
00598 
00599 /* ****************** Rearrange Channels Operator ******************* */
00600 
00601 /* constants for channel rearranging */
00602 /* WARNING: don't change exising ones without modifying rearrange func accordingly */
00603 enum {
00604     REARRANGE_ANIMCHAN_TOP= -2,
00605     REARRANGE_ANIMCHAN_UP= -1,
00606     REARRANGE_ANIMCHAN_DOWN= 1,
00607     REARRANGE_ANIMCHAN_BOTTOM= 2
00608 };
00609 
00610 /* defines for rearranging channels */
00611 static EnumPropertyItem prop_animchannel_rearrange_types[] = {
00612     {REARRANGE_ANIMCHAN_TOP, "TOP", 0, "To Top", ""},
00613     {REARRANGE_ANIMCHAN_UP, "UP", 0, "Up", ""},
00614     {REARRANGE_ANIMCHAN_DOWN, "DOWN", 0, "Down", ""},
00615     {REARRANGE_ANIMCHAN_BOTTOM, "BOTTOM", 0, "To Bottom", ""},
00616     {0, NULL, 0, NULL, NULL}
00617 };
00618 
00619 /* Reordering "Islands" Defines ----------------------------------- */
00620 
00621 /* Island definition - just a listbase container */
00622 typedef struct tReorderChannelIsland {
00623     struct tReorderChannelIsland *next, *prev;
00624     
00625     ListBase channels;  /* channels within this region with the same state */
00626     int flag;           /* eReorderIslandFlag */
00627 } tReorderChannelIsland;
00628 
00629 /* flags for channel reordering islands */
00630 typedef enum eReorderIslandFlag {
00631     REORDER_ISLAND_SELECTED         = (1<<0),   /* island is selected */
00632     REORDER_ISLAND_UNTOUCHABLE      = (1<<1),   /* island should be ignored */
00633     REORDER_ISLAND_MOVED            = (1<<2)    /* island has already been moved */
00634 } eReorderIslandFlag;
00635 
00636 
00637 /* Rearrange Methods --------------------------------------------- */
00638 
00639 static short rearrange_island_ok (tReorderChannelIsland *island)
00640 {
00641     /* island must not be untouchable */
00642     if (island->flag & REORDER_ISLAND_UNTOUCHABLE)
00643         return 0;
00644     
00645     /* island should be selected to be moved */
00646     return (island->flag & REORDER_ISLAND_SELECTED) && !(island->flag & REORDER_ISLAND_MOVED);
00647 }
00648 
00649 /* ............................. */
00650 
00651 static short rearrange_island_top (ListBase *list, tReorderChannelIsland *island)
00652 {
00653     if (rearrange_island_ok(island)) {
00654         /* remove from current position */
00655         BLI_remlink(list, island);
00656         
00657         /* make it first element */
00658         BLI_insertlinkbefore(list, list->first, island);
00659         
00660         return 1;
00661     }
00662     
00663     return 0;
00664 }
00665 
00666 static short rearrange_island_up (ListBase *list, tReorderChannelIsland *island)
00667 {
00668     if (rearrange_island_ok(island)) {
00669         /* moving up = moving before the previous island, otherwise we're in the same place */
00670         tReorderChannelIsland *prev= island->prev;
00671         
00672         if (prev) {
00673             /* remove from current position */
00674             BLI_remlink(list, island);
00675             
00676             /* push it up */
00677             BLI_insertlinkbefore(list, prev, island);
00678             
00679             return 1;
00680         }
00681     }
00682     
00683     return 0;
00684 }
00685 
00686 static short rearrange_island_down (ListBase *list, tReorderChannelIsland *island)
00687 {
00688     if (rearrange_island_ok(island)) {
00689         /* moving down = moving after the next island, otherwise we're in the same place */
00690         tReorderChannelIsland *next = island->next;
00691         
00692         if (next) {
00693             /* can only move past if next is not untouchable (i.e. nothing can go after it) */
00694             if ((next->flag & REORDER_ISLAND_UNTOUCHABLE)==0) {
00695                 /* remove from current position */
00696                 BLI_remlink(list, island);
00697                 
00698                 /* push it down */
00699                 BLI_insertlinkafter(list, next, island);
00700                 
00701                 return 1;
00702             }
00703         }
00704         /* else: no next channel, so we're at the bottom already, so can't move */
00705     }
00706     
00707     return 0;
00708 }
00709 
00710 static short rearrange_island_bottom (ListBase *list, tReorderChannelIsland *island)
00711 {
00712     if (rearrange_island_ok(island)) {
00713         tReorderChannelIsland *last = list->last;
00714         
00715         /* remove island from current position */
00716         BLI_remlink(list, island);
00717         
00718         /* add before or after the last channel? */
00719         if ((last->flag & REORDER_ISLAND_UNTOUCHABLE)==0) {
00720             /* can add after it */
00721             BLI_addtail(list, island);
00722         }
00723         else {
00724             /* can at most go just before it, since last cannot be moved */
00725             BLI_insertlinkbefore(list, last, island);
00726             
00727         }
00728         
00729         return 1;
00730     }
00731     
00732     return 0;
00733 }
00734 
00735 /* ............................. */
00736 
00737 /* typedef for channel rearranging function 
00738  * < list: list that channels belong to
00739  * < island: island to be moved
00740  * > return[0]: whether operation was a success
00741  */
00742 typedef short (*AnimChanRearrangeFp)(ListBase *list, tReorderChannelIsland *island);
00743 
00744 /* get rearranging function, given 'rearrange' mode */
00745 static AnimChanRearrangeFp rearrange_get_mode_func (short mode)
00746 {
00747     switch (mode) {
00748         case REARRANGE_ANIMCHAN_TOP:
00749             return rearrange_island_top;
00750         case REARRANGE_ANIMCHAN_UP:
00751             return rearrange_island_up;
00752         case REARRANGE_ANIMCHAN_DOWN:
00753             return rearrange_island_down;
00754         case REARRANGE_ANIMCHAN_BOTTOM:
00755             return rearrange_island_bottom;
00756         default:
00757             return NULL;
00758     }
00759 }
00760 
00761 /* Rearrange Islands Generics ------------------------------------- */
00762 
00763 /* add channel into list of islands */
00764 static void rearrange_animchannel_add_to_islands (ListBase *islands, ListBase *srcList, Link *channel, short type)
00765 {
00766     tReorderChannelIsland *island = islands->last;  /* always try to add to last island if possible */
00767     short is_sel=0, is_untouchable=0;
00768     
00769     /* get flags - selected and untouchable from the channel */
00770     switch (type) {
00771         case ANIMTYPE_GROUP:
00772         {
00773             bActionGroup *agrp= (bActionGroup *)channel;
00774             
00775             is_sel= SEL_AGRP(agrp);
00776             is_untouchable= (agrp->flag & AGRP_TEMP) != 0;
00777         }
00778             break;
00779         case ANIMTYPE_FCURVE:
00780         {
00781             FCurve *fcu= (FCurve *)channel;
00782             
00783             is_sel= SEL_FCU(fcu);
00784         }   
00785             break;
00786         case ANIMTYPE_NLATRACK:
00787         {
00788             NlaTrack *nlt= (NlaTrack *)channel;
00789             
00790             is_sel= SEL_NLT(nlt);
00791         }
00792             break;
00793             
00794         default:
00795             printf("rearrange_animchannel_add_to_islands(): don't know how to handle channels of type %d\n", type);
00796             return;
00797     }
00798     
00799     /* do we need to add to a new island? */
00800     if ((island == NULL) ||                                 /* 1) no islands yet */
00801         ((island->flag & REORDER_ISLAND_SELECTED) == 0) ||  /* 2) unselected islands have single channels only - to allow up/down movement */
00802         (is_sel == 0))                                      /* 3) if channel is unselected, stop existing island (it was either wrong sel status, or full already) */
00803     {
00804         /* create a new island now */
00805         island = MEM_callocN(sizeof(tReorderChannelIsland), "tReorderChannelIsland");
00806         BLI_addtail(islands, island);
00807         
00808         if (is_sel)
00809             island->flag |= REORDER_ISLAND_SELECTED;
00810         if (is_untouchable)
00811             island->flag |= REORDER_ISLAND_UNTOUCHABLE;
00812     }
00813 
00814     /* add channel to island - need to remove it from its existing list first though */
00815     BLI_remlink(srcList, channel);
00816     BLI_addtail(&island->channels, channel);
00817 }
00818 
00819 /* flatten islands out into a single list again */
00820 static void rearrange_animchannel_flatten_islands (ListBase *islands, ListBase *srcList)
00821 {
00822     tReorderChannelIsland *island, *isn=NULL;
00823     
00824     /* make sure srcList is empty now */
00825     BLI_assert(srcList->first == NULL);
00826     
00827     /* go through merging islands */
00828     for (island = islands->first; island; island = isn) {
00829         isn = island->next;
00830         
00831         /* merge island channels back to main list, then delete the island */
00832         BLI_movelisttolist(srcList, &island->channels);
00833         BLI_freelinkN(islands, island);
00834     }
00835 }
00836 
00837 /* ............................. */
00838 
00839 /* performing rearranging of channels using islands */
00840 static short rearrange_animchannel_islands (ListBase *list, AnimChanRearrangeFp rearrange_func, short mode, short type)
00841 {
00842     ListBase islands = {NULL, NULL};
00843     Link *channel, *chanNext=NULL;
00844     short done = 0;
00845     
00846     /* don't waste effort on an empty list */
00847     if (list->first == NULL)
00848         return 0;
00849     
00850     /* group channels into islands */
00851     for (channel = list->first; channel; channel = chanNext) {
00852         chanNext = channel->next;
00853         rearrange_animchannel_add_to_islands(&islands, list, channel, type);
00854     }
00855     
00856     /* perform moving of selected islands now, but only if there is more than one of 'em so that something will happen 
00857      *  - scanning of the list is performed in the opposite direction to the direction we're moving things, so that we 
00858      *    shouldn't need to encounter items we've moved already
00859      */
00860     if (islands.first != islands.last) {
00861         tReorderChannelIsland *first = (mode > 0) ? islands.last : islands.first;
00862         tReorderChannelIsland *island, *isn=NULL;
00863         
00864         for (island = first; island; island = isn) {
00865             isn = (mode > 0) ? island->prev : island->next;
00866             
00867             /* perform rearranging */
00868             if (rearrange_func(&islands, island)) {
00869                 island->flag |= REORDER_ISLAND_MOVED;
00870                 done = 1;
00871             }
00872         }
00873     }
00874     
00875     /* ungroup islands */
00876     rearrange_animchannel_flatten_islands(&islands, list);
00877     
00878     /* did we do anything? */
00879     return done;
00880 }
00881 
00882 /* NLA Specific Stuff ----------------------------------------------------- */
00883 
00884 /* Change the order NLA Tracks within NLA Stack
00885  * ! NLA tracks are displayed in opposite order, so directions need care
00886  *  mode: REARRANGE_ANIMCHAN_*  
00887  */
00888 static void rearrange_nla_channels (bAnimContext *UNUSED(ac), AnimData *adt, short mode)
00889 {
00890     AnimChanRearrangeFp rearrange_func;
00891     
00892     /* hack: invert mode so that functions will work in right order */
00893     mode *= -1;
00894     
00895     /* get rearranging function */
00896     rearrange_func = rearrange_get_mode_func(mode);
00897     if (rearrange_func == NULL)
00898         return;
00899     
00900     /* only consider NLA data if it's accessible */ 
00901     //if (EXPANDED_DRVD(adt) == 0)
00902     //  return;
00903     
00904     /* perform rearranging on tracks list */
00905     rearrange_animchannel_islands(&adt->nla_tracks, rearrange_func, mode, ANIMTYPE_NLATRACK);
00906 }
00907 
00908 /* Drivers Specific Stuff ------------------------------------------------- */
00909 
00910 /* Change the order drivers within AnimData block
00911  *  mode: REARRANGE_ANIMCHAN_*  
00912  */
00913 static void rearrange_driver_channels (bAnimContext *UNUSED(ac), AnimData *adt, short mode)
00914 {
00915     /* get rearranging function */
00916     AnimChanRearrangeFp rearrange_func = rearrange_get_mode_func(mode);
00917     
00918     if (rearrange_func == NULL)
00919         return;
00920     
00921     /* only consider drivers if they're accessible */   
00922     if (EXPANDED_DRVD(adt) == 0)
00923         return;
00924     
00925     /* perform rearranging on drivers list (drivers are really just F-Curves) */
00926     rearrange_animchannel_islands(&adt->drivers, rearrange_func, mode, ANIMTYPE_FCURVE);
00927 }
00928 
00929 /* Action Specific Stuff ------------------------------------------------- */
00930 
00931 /* make sure all action-channels belong to a group (and clear action's list) */
00932 static void split_groups_action_temp (bAction *act, bActionGroup *tgrp)
00933 {
00934     bActionGroup *agrp;
00935     FCurve *fcu;
00936     
00937     if (act == NULL)
00938         return;
00939     
00940     /* Separate F-Curves into lists per group */
00941     for (agrp= act->groups.first; agrp; agrp= agrp->next) {
00942         if (agrp->channels.first) {
00943             fcu= agrp->channels.last;
00944             act->curves.first= fcu->next;
00945             
00946             fcu= agrp->channels.first;
00947             fcu->prev= NULL;
00948             
00949             fcu= agrp->channels.last;
00950             fcu->next= NULL;
00951         }
00952     }
00953     
00954     /* Initialise memory for temp-group */
00955     memset(tgrp, 0, sizeof(bActionGroup));
00956     tgrp->flag |= (AGRP_EXPANDED|AGRP_TEMP);
00957     BLI_strncpy(tgrp->name, "#TempGroup", sizeof(tgrp->name));
00958     
00959     /* Move any action-channels not already moved, to the temp group */
00960     if (act->curves.first) {
00961         /* start of list */
00962         fcu= act->curves.first;
00963         fcu->prev= NULL;
00964         tgrp->channels.first= fcu;
00965         act->curves.first= NULL;
00966         
00967         /* end of list */
00968         fcu= act->curves.last;
00969         fcu->next= NULL;
00970         tgrp->channels.last= fcu;
00971         act->curves.last= NULL;
00972     }
00973     
00974     /* Add temp-group to list */
00975     BLI_addtail(&act->groups, tgrp);
00976 }
00977 
00978 /* link lists of channels that groups have */
00979 static void join_groups_action_temp (bAction *act)
00980 {
00981     bActionGroup *agrp;
00982     
00983     for (agrp= act->groups.first; agrp; agrp= agrp->next) {
00984         ListBase tempGroup;
00985         
00986         /* add list of channels to action's channels */
00987         tempGroup= agrp->channels;
00988         BLI_movelisttolist(&act->curves, &agrp->channels);
00989         agrp->channels= tempGroup;
00990         
00991         /* clear moved flag */
00992         agrp->flag &= ~AGRP_MOVED;
00993         
00994         /* if temp-group... remove from list (but don't free as it's on the stack!) */
00995         if (agrp->flag & AGRP_TEMP) {
00996             BLI_remlink(&act->groups, agrp);
00997             break;
00998         }
00999     }
01000 }
01001 
01002 /* Change the order of anim-channels within action 
01003  *  mode: REARRANGE_ANIMCHAN_*  
01004  */
01005 static void rearrange_action_channels (bAnimContext *ac, bAction *act, short mode)
01006 {
01007     bActionGroup tgrp;
01008     short do_channels;
01009     
01010     /* get rearranging function */
01011     AnimChanRearrangeFp rearrange_func = rearrange_get_mode_func(mode);
01012     
01013     if (rearrange_func == NULL)
01014         return;
01015     
01016     /* make sure we're only operating with groups (vs a mixture of groups+curves) */
01017     split_groups_action_temp(act, &tgrp);
01018     
01019     /* rearrange groups first 
01020      *  - the group's channels will only get considered if nothing happened when rearranging the groups
01021      *    i.e. the rearrange function returned 0
01022      */
01023     do_channels = rearrange_animchannel_islands(&act->groups, rearrange_func, mode, ANIMTYPE_GROUP) == 0;
01024     
01025     if (do_channels) {
01026         bActionGroup *agrp;
01027         
01028         for (agrp= act->groups.first; agrp; agrp= agrp->next) {
01029             /* only consider F-Curves if they're visible (group expanded) */
01030             if (EXPANDED_AGRP(ac, agrp)) {
01031                 rearrange_animchannel_islands(&agrp->channels, rearrange_func, mode, ANIMTYPE_FCURVE);
01032             }
01033         }
01034     }
01035     
01036     /* assemble lists into one list (and clear moved tags) */
01037     join_groups_action_temp(act);
01038 }
01039 
01040 /* ------------------- */
01041 
01042 static int animchannels_rearrange_exec(bContext *C, wmOperator *op)
01043 {
01044     bAnimContext ac;
01045     short mode;
01046     
01047     /* get editor data */
01048     if (ANIM_animdata_get_context(C, &ac) == 0)
01049         return OPERATOR_CANCELLED;
01050         
01051     /* get mode */
01052     mode= RNA_enum_get(op->ptr, "direction");
01053     
01054     /* method to move channels depends on the editor */
01055     if (ac.datatype == ANIMCONT_GPENCIL) {
01056         /* Grease Pencil channels */
01057         printf("Grease Pencil not supported for moving yet\n");
01058     }
01059     else if (ac.datatype == ANIMCONT_ACTION) {
01060         /* Directly rearrange action's channels */
01061         rearrange_action_channels(&ac, ac.data, mode);
01062     }
01063     else {
01064         ListBase anim_data = {NULL, NULL};
01065         bAnimListElem *ale;
01066         int filter;
01067         
01068         /* get animdata blocks */
01069         filter= (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_ANIMDATA);
01070         ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
01071         
01072         for (ale = anim_data.first; ale; ale = ale->next) {
01073             AnimData *adt= ale->data;
01074             
01075             switch (ac.datatype) {
01076                 case ANIMCONT_NLA: /* NLA-tracks only */
01077                     rearrange_nla_channels(&ac, adt, mode);
01078                     break;
01079                 
01080                 case ANIMCONT_DRIVERS: /* Drivers list only */
01081                     rearrange_driver_channels(&ac, adt, mode);
01082                     break;
01083                 
01084                 case ANIMCONT_SHAPEKEY: // DOUBLE CHECK ME...
01085                     
01086                 default: /* some collection of actions */
01087                     if (adt->action)
01088                         rearrange_action_channels(&ac, adt->action, mode);
01089                     else if (G.f & G_DEBUG)
01090                         printf("Animdata has no action\n");
01091                     break;
01092             }
01093         }
01094         
01095         /* free temp data */
01096         BLI_freelistN(&anim_data);
01097     }
01098     
01099     /* send notifier that things have changed */
01100     WM_event_add_notifier(C, NC_ANIMATION|ND_ANIMCHAN|NA_EDITED, NULL);
01101     
01102     return OPERATOR_FINISHED;
01103 }
01104 
01105 static void ANIM_OT_channels_move (wmOperatorType *ot)
01106 {
01107     /* identifiers */
01108     ot->name= "Move Channels";
01109     ot->idname= "ANIM_OT_channels_move";
01110     ot->description = "Rearrange selected animation channels";
01111     
01112     /* api callbacks */
01113     ot->exec= animchannels_rearrange_exec;
01114     ot->poll= animedit_poll_channels_nla_tweakmode_off;
01115     
01116     /* flags */
01117     ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
01118     
01119     /* props */
01120     ot->prop= RNA_def_enum(ot->srna, "direction", prop_animchannel_rearrange_types, REARRANGE_ANIMCHAN_DOWN, "Direction", "");
01121 }
01122 
01123 /* ******************** Delete Channel Operator *********************** */
01124 
01125 static int animchannels_delete_exec(bContext *C, wmOperator *UNUSED(op))
01126 {
01127     bAnimContext ac;
01128     ListBase anim_data = {NULL, NULL};
01129     bAnimListElem *ale;
01130     int filter;
01131     
01132     /* get editor data */
01133     if (ANIM_animdata_get_context(C, &ac) == 0)
01134         return OPERATOR_CANCELLED;
01135     
01136     /* cannot delete in shapekey */
01137     if (ac.datatype == ANIMCONT_SHAPEKEY) 
01138         return OPERATOR_CANCELLED;
01139         
01140         
01141     /* do groups only first (unless in Drivers mode, where there are none) */
01142     if (ac.datatype != ANIMCONT_DRIVERS) {
01143         /* filter data */
01144         filter= (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_SEL | ANIMFILTER_LIST_CHANNELS | ANIMFILTER_FOREDIT | ANIMFILTER_NODUPLIS);
01145         ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
01146         
01147         /* delete selected groups and their associated channels */
01148         for (ale= anim_data.first; ale; ale= ale->next) {
01149             /* only groups - don't check other types yet, since they may no-longer exist */
01150             if (ale->type == ANIMTYPE_GROUP) {
01151                 bActionGroup *agrp= (bActionGroup *)ale->data;
01152                 AnimData *adt= ale->adt;
01153                 FCurve *fcu, *fcn;
01154                 
01155                 /* skip this group if no AnimData available, as we can't safely remove the F-Curves */
01156                 if (adt == NULL)
01157                     continue;
01158                 
01159                 /* delete all of the Group's F-Curves, but no others */
01160                 for (fcu= agrp->channels.first; fcu && fcu->grp==agrp; fcu= fcn) {
01161                     fcn= fcu->next;
01162                     
01163                     /* remove from group and action, then free */
01164                     action_groups_remove_channel(adt->action, fcu);
01165                     free_fcurve(fcu);
01166                 }
01167                 
01168                 /* free the group itself */
01169                 if (adt->action)
01170                     BLI_freelinkN(&adt->action->groups, agrp);
01171                 else
01172                     MEM_freeN(agrp);
01173             }
01174         }
01175         
01176         /* cleanup */
01177         BLI_freelistN(&anim_data);
01178     }
01179     
01180     /* filter data */
01181     filter= (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_SEL | ANIMFILTER_FOREDIT | ANIMFILTER_NODUPLIS);
01182     ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
01183     
01184     /* delete selected data channels */
01185     for (ale= anim_data.first; ale; ale= ale->next) {
01186         switch (ale->type) {
01187             case ANIMTYPE_FCURVE: 
01188             {
01189                 /* F-Curves if we can identify its parent */
01190                 AnimData *adt= ale->adt;
01191                 FCurve *fcu= (FCurve *)ale->data;
01192                 
01193                 /* try to free F-Curve */
01194                 ANIM_fcurve_delete_from_animdata(&ac, adt, fcu);
01195             }
01196                 break;
01197                 
01198             case ANIMTYPE_GPLAYER:
01199             {
01200                 /* Grease Pencil layer */
01201                 bGPdata *gpd= (bGPdata *)ale->id;
01202                 bGPDlayer *gpl= (bGPDlayer *)ale->data;
01203                 
01204                 /* try to delete the layer's data and the layer itself */
01205                 free_gpencil_frames(gpl);
01206                 BLI_freelinkN(&gpd->layers, gpl);
01207             }
01208                 break;
01209         }
01210     }
01211     
01212     /* cleanup */
01213     BLI_freelistN(&anim_data);
01214     
01215     /* send notifier that things have changed */
01216     WM_event_add_notifier(C, NC_ANIMATION|ND_ANIMCHAN|NA_EDITED, NULL);
01217     
01218     return OPERATOR_FINISHED;
01219 }
01220  
01221 static void ANIM_OT_channels_delete (wmOperatorType *ot)
01222 {
01223     /* identifiers */
01224     ot->name= "Delete Channels";
01225     ot->idname= "ANIM_OT_channels_delete";
01226     ot->description= "Delete all selected animation channels";
01227     
01228     /* api callbacks */
01229     ot->exec= animchannels_delete_exec;
01230     ot->poll= animedit_poll_channels_active;
01231     
01232     /* flags */
01233     ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
01234 }
01235 
01236 /* ******************** Set Channel Visibility Operator *********************** */
01237 /* NOTE: this operator is only valid in the Graph Editor channels region */
01238 
01239 static int animchannels_visibility_set_exec(bContext *C, wmOperator *UNUSED(op))
01240 {
01241     bAnimContext ac;
01242     ListBase anim_data = {NULL, NULL};
01243     ListBase all_data = {NULL, NULL};
01244     bAnimListElem *ale;
01245     int filter;
01246     
01247     /* get editor data */
01248     if (ANIM_animdata_get_context(C, &ac) == 0)
01249         return OPERATOR_CANCELLED;
01250     
01251     /* get list of all channels that selection may need to be flushed to 
01252      * - hierarchy mustn't affect what we have access to here...
01253      */
01254     filter= (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_CHANNELS | ANIMFILTER_NODUPLIS);
01255     ANIM_animdata_filter(&ac, &all_data, filter, ac.data, ac.datatype);
01256         
01257     /* hide all channels not selected
01258      * - hierarchy matters if we're doing this from the channels region
01259      *   since we only want to apply this to channels we can "see", 
01260      *   and have these affect their relatives
01261      * - but for Graph Editor, this gets used also from main region
01262      *   where hierarchy doesn't apply, as for [#21276]
01263      */
01264     if ((ac.spacetype == SPACE_IPO) && (ac.regiontype != RGN_TYPE_CHANNELS)) {
01265         /* graph editor (case 2) */
01266         filter= (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_UNSEL | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_NODUPLIS);
01267     }
01268     else {
01269         /* standard case */
01270         filter= (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_UNSEL | ANIMFILTER_NODUPLIS);
01271     }
01272     ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
01273     
01274     for (ale= anim_data.first; ale; ale= ale->next) {
01275         /* clear setting first */
01276         ANIM_channel_setting_set(&ac, ale, ACHANNEL_SETTING_VISIBLE, ACHANNEL_SETFLAG_CLEAR);
01277         
01278         /* now also flush selection status as appropriate 
01279          * NOTE: in some cases, this may result in repeat flushing being performed
01280          */
01281         ANIM_flush_setting_anim_channels(&ac, &all_data, ale, ACHANNEL_SETTING_VISIBLE, 0);
01282     }
01283     
01284     BLI_freelistN(&anim_data);
01285     
01286     /* make all the selected channels visible */
01287     filter= (ANIMFILTER_SEL | ANIMFILTER_NODUPLIS);
01288     ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
01289     
01290     for (ale= anim_data.first; ale; ale= ale->next) {
01291         /* hack: skip object channels for now, since flushing those will always flush everything, but they are always included */
01292         // TODO: find out why this is the case, and fix that
01293         if (ale->type == ANIMTYPE_OBJECT)
01294             continue;
01295         
01296         /* enable the setting */
01297         ANIM_channel_setting_set(&ac, ale, ACHANNEL_SETTING_VISIBLE, ACHANNEL_SETFLAG_ADD);
01298         
01299         /* now, also flush selection status up/down as appropriate */
01300         ANIM_flush_setting_anim_channels(&ac, &all_data, ale, ACHANNEL_SETTING_VISIBLE, 1);
01301     }
01302     
01303     BLI_freelistN(&anim_data);
01304     BLI_freelistN(&all_data);
01305     
01306     
01307     /* send notifier that things have changed */
01308     WM_event_add_notifier(C, NC_ANIMATION|ND_ANIMCHAN|NA_EDITED, NULL);
01309     
01310     return OPERATOR_FINISHED;
01311 }
01312 
01313 static void ANIM_OT_channels_visibility_set (wmOperatorType *ot)
01314 {
01315     /* identifiers */
01316     ot->name= "Set Visibility";
01317     ot->idname= "ANIM_OT_channels_visibility_set";
01318     ot->description= "Make only the selected animation channels visible in the Graph Editor";
01319     
01320     /* api callbacks */
01321     ot->exec= animchannels_visibility_set_exec;
01322     ot->poll= ED_operator_graphedit_active;
01323     
01324     /* flags */
01325     ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
01326 }
01327 
01328 
01329 /* ******************** Toggle Channel Visibility Operator *********************** */
01330 /* NOTE: this operator is only valid in the Graph Editor channels region */
01331 
01332 static int animchannels_visibility_toggle_exec(bContext *C, wmOperator *UNUSED(op))
01333 {
01334     bAnimContext ac;
01335     ListBase anim_data = {NULL, NULL};
01336     ListBase all_data = {NULL, NULL};
01337     bAnimListElem *ale;
01338     int filter;
01339     short vis= ACHANNEL_SETFLAG_ADD;
01340     
01341     /* get editor data */
01342     if (ANIM_animdata_get_context(C, &ac) == 0)
01343         return OPERATOR_CANCELLED;
01344         
01345     /* get list of all channels that selection may need to be flushed to 
01346      * - hierarchy mustn't affect what we have access to here...
01347      */
01348     filter= (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_CHANNELS | ANIMFILTER_NODUPLIS);
01349     ANIM_animdata_filter(&ac, &all_data, filter, ac.data, ac.datatype);
01350         
01351     /* filter data
01352      * - restrict this to only applying on settings we can get to in the list
01353      */
01354     filter= (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_SEL | ANIMFILTER_NODUPLIS);
01355     ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
01356     
01357     /* See if we should be making showing all selected or hiding */
01358     for (ale= anim_data.first; ale; ale= ale->next) {
01359         /* set the setting in the appropriate way (if available) */
01360         if (ANIM_channel_setting_get(&ac, ale, ACHANNEL_SETTING_VISIBLE)) {
01361             vis= ACHANNEL_SETFLAG_CLEAR;
01362             break;
01363         }
01364     }
01365 
01366     /* Now set the flags */
01367     for (ale= anim_data.first; ale; ale= ale->next) {
01368         /* hack: skip object channels for now, since flushing those will always flush everything, but they are always included */
01369         // TODO: find out why this is the case, and fix that
01370         if (ale->type == ANIMTYPE_OBJECT)
01371             continue;
01372         
01373         /* change the setting */
01374         ANIM_channel_setting_set(&ac, ale, ACHANNEL_SETTING_VISIBLE, vis);
01375         
01376         /* now, also flush selection status up/down as appropriate */
01377         ANIM_flush_setting_anim_channels(&ac, &all_data, ale, ACHANNEL_SETTING_VISIBLE, (vis == ACHANNEL_SETFLAG_ADD));
01378     }
01379     
01380     /* cleanup */
01381     BLI_freelistN(&anim_data);
01382     BLI_freelistN(&all_data);
01383     
01384     /* send notifier that things have changed */
01385     WM_event_add_notifier(C, NC_ANIMATION|ND_ANIMCHAN|NA_EDITED, NULL);
01386     
01387     return OPERATOR_FINISHED;
01388 }
01389 
01390 static void ANIM_OT_channels_visibility_toggle (wmOperatorType *ot)
01391 {
01392     /* identifiers */
01393     ot->name= "Toggle Visibility";
01394     ot->idname= "ANIM_OT_channels_visibility_toggle";
01395     ot->description= "Toggle visibility in Graph Editor of all selected animation channels";
01396     
01397     /* api callbacks */
01398     ot->exec= animchannels_visibility_toggle_exec;
01399     ot->poll= ED_operator_graphedit_active;
01400     
01401     /* flags */
01402     ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
01403 }
01404 
01405 /* ********************** Set Flags Operator *********************** */
01406 
01407 /* defines for setting animation-channel flags */
01408 static EnumPropertyItem prop_animchannel_setflag_types[] = {
01409     {ACHANNEL_SETFLAG_TOGGLE, "TOGGLE", 0, "Toggle", ""},
01410     {ACHANNEL_SETFLAG_CLEAR, "DISABLE", 0, "Disable", ""},
01411     {ACHANNEL_SETFLAG_ADD, "ENABLE", 0, "Enable", ""},
01412     {ACHANNEL_SETFLAG_INVERT, "INVERT", 0, "Invert", ""},
01413     {0, NULL, 0, NULL, NULL}
01414 };
01415 
01416 /* defines for set animation-channel settings */
01417 // TODO: could add some more types, but those are really quite dependent on the mode...
01418 static EnumPropertyItem prop_animchannel_settings_types[] = {
01419     {ACHANNEL_SETTING_PROTECT, "PROTECT", 0, "Protect", ""},
01420     {ACHANNEL_SETTING_MUTE, "MUTE", 0, "Mute", ""},
01421     {0, NULL, 0, NULL, NULL}
01422 };
01423 
01424 
01425 /* ------------------- */
01426 
01427 /* Set/clear a particular flag (setting) for all selected + visible channels 
01428  *  setting: the setting to modify
01429  *  mode: eAnimChannels_SetFlag
01430  *  onlysel: only selected channels get the flag set
01431  */
01432 // TODO: enable a setting which turns flushing on/off?
01433 static void setflag_anim_channels (bAnimContext *ac, short setting, short mode, short onlysel, short flush)
01434 {
01435     ListBase anim_data = {NULL, NULL};
01436     ListBase all_data = {NULL, NULL};
01437     bAnimListElem *ale;
01438     int filter;
01439     
01440     /* filter data that we need if flush is on */
01441     if (flush) {
01442         /* get list of all channels that selection may need to be flushed to 
01443          * - hierarchy visibility needs to be ignored so that settings can get flushed
01444          *   "down" inside closed containers
01445          */
01446         filter= ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_CHANNELS;
01447         ANIM_animdata_filter(ac, &all_data, filter, ac->data, ac->datatype);
01448     }
01449     
01450     /* filter data that we're working on 
01451      * - hierarchy matters if we're doing this from the channels region
01452      *   since we only want to apply this to channels we can "see", 
01453      *   and have these affect their relatives
01454      * - but for Graph Editor, this gets used also from main region
01455      *   where hierarchy doesn't apply [#21276]
01456      */
01457     if ((ac->spacetype == SPACE_IPO) && (ac->regiontype != RGN_TYPE_CHANNELS)) {
01458         /* graph editor (case 2) */
01459         filter= (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_CHANNELS | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_NODUPLIS);
01460     }
01461     else {
01462         /* standard case */
01463         filter= (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_LIST_CHANNELS | ANIMFILTER_NODUPLIS);
01464     }
01465     if (onlysel) filter |= ANIMFILTER_SEL;
01466     ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
01467     
01468     /* if toggling, check if disable or enable */
01469     if (mode == ACHANNEL_SETFLAG_TOGGLE) {
01470         /* default to turn all on, unless we encounter one that's on... */
01471         mode= ACHANNEL_SETFLAG_ADD;
01472         
01473         /* see if we should turn off instead... */
01474         for (ale= anim_data.first; ale; ale= ale->next) {
01475             /* set the setting in the appropriate way (if available) */
01476             if (ANIM_channel_setting_get(ac, ale, setting) > 0) {
01477                 mode= ACHANNEL_SETFLAG_CLEAR;
01478                 break;
01479             }
01480         }
01481     }
01482     
01483     /* apply the setting */
01484     for (ale= anim_data.first; ale; ale= ale->next) {
01485         /* skip channel if setting is not available */
01486         if (ANIM_channel_setting_get(ac, ale, setting) == -1)
01487             continue;
01488         
01489         /* set the setting in the appropriate way */
01490         ANIM_channel_setting_set(ac, ale, setting, mode);
01491         
01492         /* if flush status... */
01493         if (flush)
01494             ANIM_flush_setting_anim_channels(ac, &all_data, ale, setting, mode);
01495     }
01496     
01497     BLI_freelistN(&anim_data);
01498     BLI_freelistN(&all_data);
01499 }
01500 
01501 /* ------------------- */
01502 
01503 static int animchannels_setflag_exec(bContext *C, wmOperator *op)
01504 {
01505     bAnimContext ac;
01506     short mode, setting;
01507     short flush=1;
01508     
01509     /* get editor data */
01510     if (ANIM_animdata_get_context(C, &ac) == 0)
01511         return OPERATOR_CANCELLED;
01512         
01513     /* mode (eAnimChannels_SetFlag), setting (eAnimChannel_Settings) */
01514     mode= RNA_enum_get(op->ptr, "mode");
01515     setting= RNA_enum_get(op->ptr, "type");
01516     
01517     /* check if setting is flushable */
01518     if (setting == ACHANNEL_SETTING_EXPAND)
01519         flush= 0;
01520     
01521     /* modify setting 
01522      *  - only selected channels are affected
01523      */
01524     setflag_anim_channels(&ac, setting, mode, 1, flush);
01525     
01526     /* send notifier that things have changed */
01527     WM_event_add_notifier(C, NC_ANIMATION|ND_ANIMCHAN|NA_EDITED, NULL);
01528     
01529     return OPERATOR_FINISHED;
01530 }
01531 
01532 
01533 static void ANIM_OT_channels_setting_enable (wmOperatorType *ot)
01534 {
01535     /* identifiers */
01536     ot->name= "Enable Channel Setting";
01537     ot->idname= "ANIM_OT_channels_setting_enable";
01538     ot->description= "Enable specified setting on all selected animation channels";
01539     
01540     /* api callbacks */
01541     ot->invoke= WM_menu_invoke;
01542     ot->exec= animchannels_setflag_exec;
01543     ot->poll= animedit_poll_channels_active;
01544     
01545     /* flags */
01546     ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
01547     
01548     /* props */
01549         /* flag-setting mode */
01550     RNA_def_enum(ot->srna, "mode", prop_animchannel_setflag_types, ACHANNEL_SETFLAG_ADD, "Mode", "");
01551         /* setting to set */
01552     ot->prop= RNA_def_enum(ot->srna, "type", prop_animchannel_settings_types, 0, "Type", "");
01553 }
01554 
01555 static void ANIM_OT_channels_setting_disable (wmOperatorType *ot)
01556 {
01557     /* identifiers */
01558     ot->name= "Disable Channel Setting";
01559     ot->idname= "ANIM_OT_channels_setting_disable";
01560     ot->description= "Disable specified setting on all selected animation channels";
01561     
01562     /* api callbacks */
01563     ot->invoke= WM_menu_invoke;
01564     ot->exec= animchannels_setflag_exec;
01565     ot->poll= animedit_poll_channels_active;
01566     
01567     /* flags */
01568     ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
01569     
01570     /* props */
01571         /* flag-setting mode */
01572     RNA_def_enum(ot->srna, "mode", prop_animchannel_setflag_types, ACHANNEL_SETFLAG_CLEAR, "Mode", "");
01573         /* setting to set */
01574     ot->prop= RNA_def_enum(ot->srna, "type", prop_animchannel_settings_types, 0, "Type", "");
01575 }
01576 
01577 static void ANIM_OT_channels_setting_invert (wmOperatorType *ot)
01578 {
01579     /* identifiers */
01580     ot->name= "Invert Channel Setting";
01581     ot->idname= "ANIM_OT_channels_setting_toggle";
01582     ot->description= "Invert specified setting on all selected animation channels";
01583     
01584     /* api callbacks */
01585     ot->invoke= WM_menu_invoke;
01586     ot->exec= animchannels_setflag_exec;
01587     ot->poll= animedit_poll_channels_active;
01588     
01589     /* flags */
01590     ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
01591     
01592     /* props */
01593         /* flag-setting mode */
01594     RNA_def_enum(ot->srna, "mode", prop_animchannel_setflag_types, ACHANNEL_SETFLAG_INVERT, "Mode", "");
01595         /* setting to set */
01596     ot->prop= RNA_def_enum(ot->srna, "type", prop_animchannel_settings_types, 0, "Type", "");
01597 }
01598 
01599 static void ANIM_OT_channels_setting_toggle (wmOperatorType *ot)
01600 {
01601     /* identifiers */
01602     ot->name= "Toggle Channel Setting";
01603     ot->idname= "ANIM_OT_channels_setting_toggle";
01604     ot->description= "Toggle specified setting on all selected animation channels";
01605     
01606     /* api callbacks */
01607     ot->invoke= WM_menu_invoke;
01608     ot->exec= animchannels_setflag_exec;
01609     ot->poll= animedit_poll_channels_active;
01610     
01611     /* flags */
01612     ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
01613     
01614     /* props */
01615         /* flag-setting mode */
01616     RNA_def_enum(ot->srna, "mode", prop_animchannel_setflag_types, ACHANNEL_SETFLAG_TOGGLE, "Mode", "");
01617         /* setting to set */
01618     ot->prop= RNA_def_enum(ot->srna, "type", prop_animchannel_settings_types, 0, "Type", "");
01619 }
01620 
01621 static void ANIM_OT_channels_editable_toggle (wmOperatorType *ot)
01622 {
01623     /* identifiers */
01624     ot->name= "Toggle Channel Editability";
01625     ot->idname= "ANIM_OT_channels_editable_toggle";
01626     ot->description= "Toggle editability of selected channels";
01627     
01628     /* api callbacks */
01629     ot->exec= animchannels_setflag_exec;
01630     ot->poll= animedit_poll_channels_active;
01631     
01632     /* flags */
01633     ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
01634     
01635     /* props */
01636         /* flag-setting mode */
01637     RNA_def_enum(ot->srna, "mode", prop_animchannel_setflag_types, ACHANNEL_SETFLAG_TOGGLE, "Mode", "");
01638         /* setting to set */
01639     RNA_def_enum(ot->srna, "type", prop_animchannel_settings_types, ACHANNEL_SETTING_PROTECT, "Type", "");
01640 }
01641 
01642 /* ********************** Expand Channels Operator *********************** */
01643 
01644 static int animchannels_expand_exec (bContext *C, wmOperator *op)
01645 {
01646     bAnimContext ac;
01647     short onlysel= 1;
01648     
01649     /* get editor data */
01650     if (ANIM_animdata_get_context(C, &ac) == 0)
01651         return OPERATOR_CANCELLED;
01652         
01653     /* only affect selected channels? */
01654     if (RNA_boolean_get(op->ptr, "all"))
01655         onlysel= 0;
01656     
01657     /* modify setting */
01658     setflag_anim_channels(&ac, ACHANNEL_SETTING_EXPAND, ACHANNEL_SETFLAG_ADD, onlysel, 0);
01659     
01660     /* send notifier that things have changed */
01661     WM_event_add_notifier(C, NC_ANIMATION|ND_ANIMCHAN|NA_EDITED, NULL);
01662     
01663     return OPERATOR_FINISHED;
01664 }
01665 
01666 static void ANIM_OT_channels_expand (wmOperatorType *ot)
01667 {
01668     /* identifiers */
01669     ot->name= "Expand Channels";
01670     ot->idname= "ANIM_OT_channels_expand";
01671     ot->description= "Expand (i.e. open) all selected expandable animation channels";
01672     
01673     /* api callbacks */
01674     ot->exec= animchannels_expand_exec;
01675     ot->poll= animedit_poll_channels_active;
01676     
01677     /* flags */
01678     ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
01679     
01680     /* props */
01681     ot->prop= RNA_def_boolean(ot->srna, "all", 1, "All", "Expand all channels (not just selected ones)");
01682 }
01683 
01684 /* ********************** Collapse Channels Operator *********************** */
01685 
01686 static int animchannels_collapse_exec (bContext *C, wmOperator *op)
01687 {
01688     bAnimContext ac;
01689     short onlysel= 1;
01690     
01691     /* get editor data */
01692     if (ANIM_animdata_get_context(C, &ac) == 0)
01693         return OPERATOR_CANCELLED;
01694         
01695     /* only affect selected channels? */
01696     if (RNA_boolean_get(op->ptr, "all"))
01697         onlysel= 0;
01698     
01699     /* modify setting */
01700     setflag_anim_channels(&ac, ACHANNEL_SETTING_EXPAND, ACHANNEL_SETFLAG_CLEAR, onlysel, 0);
01701     
01702     /* send notifier that things have changed */
01703     WM_event_add_notifier(C, NC_ANIMATION|ND_ANIMCHAN|NA_EDITED, NULL);
01704     
01705     return OPERATOR_FINISHED;
01706 }
01707 
01708 static void ANIM_OT_channels_collapse (wmOperatorType *ot)
01709 {
01710     /* identifiers */
01711     ot->name= "Collapse Channels";
01712     ot->idname= "ANIM_OT_channels_collapse";
01713     ot->description= "Collapse (i.e. close) all selected expandable animation channels";
01714     
01715     /* api callbacks */
01716     ot->exec= animchannels_collapse_exec;
01717     ot->poll= animedit_poll_channels_active;
01718     
01719     /* flags */
01720     ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
01721     
01722     /* props */
01723     ot->prop= RNA_def_boolean(ot->srna, "all", 1, "All", "Collapse all channels (not just selected ones)");
01724 }
01725 
01726 /* ******************* Reenable Disabled Operator ******************* */
01727 
01728 static int animchannels_enable_poll (bContext *C)
01729 {
01730     ScrArea *sa= CTX_wm_area(C);
01731     
01732     /* channels region test */
01733     // TODO: could enhance with actually testing if channels region?
01734     if (ELEM(NULL, sa, CTX_wm_region(C)))
01735         return 0;
01736         
01737     /* animation editor test - Action/Dopesheet/etc. and Graph only */
01738     if (ELEM(sa->spacetype, SPACE_ACTION, SPACE_IPO) == 0)
01739         return 0;
01740         
01741     return 1;
01742 }
01743 
01744 static int animchannels_enable_exec (bContext *C, wmOperator *UNUSED(op))
01745 {
01746     bAnimContext ac;
01747     
01748     ListBase anim_data = {NULL, NULL};
01749     bAnimListElem *ale;
01750     int filter;
01751     
01752     /* get editor data */
01753     if (ANIM_animdata_get_context(C, &ac) == 0)
01754         return OPERATOR_CANCELLED;
01755     
01756     /* filter data */
01757     filter= (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_NODUPLIS);
01758     ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
01759     
01760     /* loop through filtered data and clean curves */
01761     for (ale= anim_data.first; ale; ale= ale->next) {
01762         FCurve *fcu = (FCurve *)ale->data;
01763         
01764         /* remove disabled flags from F-Curves */
01765         fcu->flag &= ~FCURVE_DISABLED;
01766         
01767         /* for drivers, let's do the same too */
01768         if (fcu->driver)
01769             fcu->driver->flag &= ~DRIVER_FLAG_INVALID;
01770             
01771         /* tag everything for updates - in particular, this is needed to get drivers working again */
01772         ANIM_list_elem_update(ac.scene, ale);
01773     }
01774     
01775     /* free temp data */
01776     BLI_freelistN(&anim_data);
01777         
01778     /* send notifier that things have changed */
01779     WM_event_add_notifier(C, NC_ANIMATION|ND_ANIMCHAN|NA_EDITED, NULL);
01780     
01781     return OPERATOR_FINISHED;
01782 }
01783 
01784 static void ANIM_OT_channels_fcurves_enable (wmOperatorType *ot)
01785 {
01786     /* identifiers */
01787     ot->name= "Revive Disabled F-Curves";
01788     ot->idname= "ANIM_OT_channels_fcurves_enable";
01789     ot->description= "Clears 'disabled' tag from all F-Curves to get broken F-Curves working again";
01790     
01791     /* api callbacks */
01792     ot->exec= animchannels_enable_exec;
01793     ot->poll= animchannels_enable_poll;
01794     
01795     /* flags */
01796     ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
01797 }
01798 
01799 /* ********************** Select All Operator *********************** */
01800 
01801 static int animchannels_deselectall_exec (bContext *C, wmOperator *op)
01802 {
01803     bAnimContext ac;
01804     
01805     /* get editor data */
01806     if (ANIM_animdata_get_context(C, &ac) == 0)
01807         return OPERATOR_CANCELLED;
01808         
01809     /* 'standard' behaviour - check if selected, then apply relevant selection */
01810     if (RNA_boolean_get(op->ptr, "invert"))
01811         ANIM_deselect_anim_channels(&ac, ac.data, ac.datatype, 0, ACHANNEL_SETFLAG_TOGGLE);
01812     else
01813         ANIM_deselect_anim_channels(&ac, ac.data, ac.datatype, 1, ACHANNEL_SETFLAG_ADD);
01814     
01815     /* send notifier that things have changed */
01816     WM_event_add_notifier(C, NC_ANIMATION|ND_ANIMCHAN|NA_SELECTED, NULL);
01817     
01818     return OPERATOR_FINISHED;
01819 }
01820  
01821 static void ANIM_OT_channels_select_all_toggle (wmOperatorType *ot)
01822 {
01823     /* identifiers */
01824     ot->name= "Select All";
01825     ot->idname= "ANIM_OT_channels_select_all_toggle";
01826     ot->description= "Toggle selection of all animation channels";
01827     
01828     /* api callbacks */
01829     ot->exec= animchannels_deselectall_exec;
01830     ot->poll= animedit_poll_channels_nla_tweakmode_off;
01831     
01832     /* flags */
01833     ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
01834     
01835     /* props */
01836     ot->prop= RNA_def_boolean(ot->srna, "invert", 0, "Invert", "");
01837 }
01838 
01839 /* ******************** Borderselect Operator *********************** */
01840 
01841 static void borderselect_anim_channels (bAnimContext *ac, rcti *rect, short selectmode)
01842 {
01843     ListBase anim_data = {NULL, NULL};
01844     bAnimListElem *ale;
01845     int filter;
01846     
01847     SpaceNla *snla = (SpaceNla *)ac->sl;
01848     View2D *v2d= &ac->ar->v2d;
01849     rctf rectf;
01850     float ymin, ymax;
01851     
01852     /* set initial y extents */
01853     if (ac->datatype == ANIMCONT_NLA) {
01854         ymin = (float)(-NLACHANNEL_HEIGHT(snla));
01855         ymax = 0.0f;
01856     }
01857     else {
01858         ymin = 0.0f;
01859         ymax = (float)(-ACHANNEL_HEIGHT);
01860     }
01861     
01862     /* convert border-region to view coordinates */
01863     UI_view2d_region_to_view(v2d, rect->xmin, rect->ymin+2, &rectf.xmin, &rectf.ymin);
01864     UI_view2d_region_to_view(v2d, rect->xmax, rect->ymax-2, &rectf.xmax, &rectf.ymax);
01865     
01866     /* filter data */
01867     filter= (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_LIST_CHANNELS);
01868     ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
01869     
01870     /* loop over data, doing border select */
01871     for (ale= anim_data.first; ale; ale= ale->next) {
01872         if (ac->datatype == ANIMCONT_NLA)
01873             ymin= ymax - NLACHANNEL_STEP(snla);
01874         else
01875             ymin= ymax - ACHANNEL_STEP;
01876         
01877         /* if channel is within border-select region, alter it */
01878         if (!((ymax < rectf.ymin) || (ymin > rectf.ymax))) {
01879             /* set selection flags only */
01880             ANIM_channel_setting_set(ac, ale, ACHANNEL_SETTING_SELECT, selectmode);
01881             
01882             /* type specific actions */
01883             switch (ale->type) {
01884                 case ANIMTYPE_GROUP:
01885                 {
01886                     bActionGroup *agrp= (bActionGroup *)ale->data;
01887                     
01888                     /* always clear active flag after doing this */
01889                     agrp->flag &= ~AGRP_ACTIVE;
01890                 }
01891                     break;
01892                 case ANIMTYPE_NLATRACK:
01893                 {
01894                     NlaTrack *nlt= (NlaTrack *)ale->data;
01895                     
01896                     /* for now, it's easier just to do this here manually, as defining a new type 
01897                      * currently adds complications when doing other stuff 
01898                      */
01899                     ACHANNEL_SET_FLAG(nlt, selectmode, NLATRACK_SELECTED);
01900                 }
01901                     break;
01902             }
01903         }
01904         
01905         /* set minimum extent to be the maximum of the next channel */
01906         ymax= ymin;
01907     }
01908     
01909     /* cleanup */
01910     BLI_freelistN(&anim_data);
01911 }
01912 
01913 /* ------------------- */
01914 
01915 static int animchannels_borderselect_exec(bContext *C, wmOperator *op)
01916 {
01917     bAnimContext ac;
01918     rcti rect;
01919     short selectmode=0;
01920     int gesture_mode, extend;
01921     
01922     /* get editor data */
01923     if (ANIM_animdata_get_context(C, &ac) == 0)
01924         return OPERATOR_CANCELLED;
01925     
01926     /* get settings from operator */
01927     rect.xmin= RNA_int_get(op->ptr, "xmin");
01928     rect.ymin= RNA_int_get(op->ptr, "ymin");
01929     rect.xmax= RNA_int_get(op->ptr, "xmax");
01930     rect.ymax= RNA_int_get(op->ptr, "ymax");
01931     
01932     gesture_mode= RNA_int_get(op->ptr, "gesture_mode");
01933     extend= RNA_boolean_get(op->ptr, "extend");
01934 
01935     if(!extend)
01936         ANIM_deselect_anim_channels(&ac, ac.data, ac.datatype, 1, ACHANNEL_SETFLAG_CLEAR);
01937 
01938     if (gesture_mode == GESTURE_MODAL_SELECT)
01939         selectmode = ACHANNEL_SETFLAG_ADD;
01940     else
01941         selectmode = ACHANNEL_SETFLAG_CLEAR;
01942     
01943     /* apply borderselect animation channels */
01944     borderselect_anim_channels(&ac, &rect, selectmode);
01945     
01946     /* send notifier that things have changed */
01947     WM_event_add_notifier(C, NC_ANIMATION|ND_ANIMCHAN|NA_SELECTED, NULL);
01948     
01949     return OPERATOR_FINISHED;
01950 } 
01951 
01952 static void ANIM_OT_channels_select_border(wmOperatorType *ot)
01953 {
01954     /* identifiers */
01955     ot->name= "Border Select";
01956     ot->idname= "ANIM_OT_channels_select_border";
01957     ot->description= "Select all animation channels within the specified region";
01958     
01959     /* api callbacks */
01960     ot->invoke= WM_border_select_invoke;
01961     ot->exec= animchannels_borderselect_exec;
01962     ot->modal= WM_border_select_modal;
01963     ot->cancel= WM_border_select_cancel;
01964     
01965     ot->poll= animedit_poll_channels_nla_tweakmode_off;
01966     
01967     /* flags */
01968     ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
01969     
01970     /* rna */
01971     WM_operator_properties_gesture_border(ot, TRUE);
01972 }
01973 
01974 /* ******************* Rename Operator ***************************** */
01975 /* Allow renaming some channels by clicking on them */
01976 
01977 static void rename_anim_channels (bAnimContext *ac, int channel_index)
01978 {
01979     ListBase anim_data = {NULL, NULL};
01980     bAnimChannelType *acf;
01981     bAnimListElem *ale;
01982     int filter;
01983     
01984     /* get the channel that was clicked on */
01985         /* filter channels */
01986     filter= (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_LIST_CHANNELS);
01987     ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
01988     
01989         /* get channel from index */
01990     ale= BLI_findlink(&anim_data, channel_index);
01991     if (ale == NULL) {
01992         /* channel not found */
01993         if (G.f & G_DEBUG)
01994             printf("Error: animation channel (index = %d) not found in rename_anim_channels() \n", channel_index);
01995         
01996         BLI_freelistN(&anim_data);
01997         return;
01998     }
01999     
02000     /* check that channel can be renamed */
02001     acf = ANIM_channel_get_typeinfo(ale);
02002     if (acf && acf->name_prop) {
02003         PointerRNA ptr;
02004         PropertyRNA *prop;
02005         
02006         /* ok if we can get name property to edit from this channel */
02007         if (acf->name_prop(ale, &ptr, &prop)) {
02008             /* actually showing the rename textfield is done on redraw,
02009              * so here we just store the index of this channel in the 
02010              * dopesheet data, which will get utilised when drawing the
02011              * channel...
02012              *
02013              * +1 factor is for backwards compat issues
02014              */
02015             if (ac->ads) {
02016                 ac->ads->renameIndex = channel_index + 1;
02017             }
02018         }
02019     }
02020     
02021     /* free temp data and tag for refresh */
02022     BLI_freelistN(&anim_data);
02023     ED_region_tag_redraw(ac->ar);
02024 }
02025 
02026 static int animchannels_rename_invoke (bContext *C, wmOperator *UNUSED(op), wmEvent *evt)
02027 {
02028     bAnimContext ac;
02029     ARegion *ar;
02030     View2D *v2d;
02031     int channel_index;
02032     float x, y;
02033     
02034     /* get editor data */
02035     if (ANIM_animdata_get_context(C, &ac) == 0)
02036         return OPERATOR_CANCELLED;
02037         
02038     /* get useful pointers from animation context data */
02039     ar= ac.ar;
02040     v2d= &ar->v2d;
02041     
02042     /* figure out which channel user clicked in 
02043      * Note: although channels technically start at y= ACHANNEL_FIRST, we need to adjust by half a channel's height
02044      *      so that the tops of channels get caught ok. Since ACHANNEL_FIRST is really ACHANNEL_HEIGHT, we simply use
02045      *      ACHANNEL_HEIGHT_HALF.
02046      */
02047     UI_view2d_region_to_view(v2d, evt->mval[0], evt->mval[1], &x, &y);
02048     
02049     if (ac.datatype == ANIMCONT_NLA) {
02050         SpaceNla *snla = (SpaceNla *)ac.sl;
02051         UI_view2d_listview_view_to_cell(v2d, NLACHANNEL_NAMEWIDTH, NLACHANNEL_STEP(snla), 0, (float)NLACHANNEL_HEIGHT_HALF(snla), x, y, NULL, &channel_index);
02052     }
02053     else {
02054         UI_view2d_listview_view_to_cell(v2d, ACHANNEL_NAMEWIDTH, ACHANNEL_STEP, 0, (float)ACHANNEL_HEIGHT_HALF, x, y, NULL, &channel_index);
02055     }
02056     
02057     /* handle click */
02058     rename_anim_channels(&ac, channel_index);
02059     
02060     return OPERATOR_FINISHED;
02061 }
02062 
02063 static void ANIM_OT_channels_rename (wmOperatorType *ot)
02064 {
02065     /* identifiers */
02066     ot->name= "Rename Channels";
02067     ot->idname= "ANIM_OT_channels_rename";
02068     ot->description= "Rename animation channel under mouse";
02069     
02070     /* api callbacks */
02071     ot->invoke= animchannels_rename_invoke;
02072     ot->poll= animedit_poll_channels_active;
02073 }
02074 
02075 /* ******************** Mouse-Click Operator *********************** */
02076 /* Handle selection changes due to clicking on channels. Settings will get caught by UI code... */
02077 
02078 static int mouse_anim_channels (bAnimContext *ac, float UNUSED(x), int channel_index, short selectmode)
02079 {
02080     ListBase anim_data = {NULL, NULL};
02081     bAnimListElem *ale;
02082     int filter;
02083     int notifierFlags = 0;
02084     
02085     /* get the channel that was clicked on */
02086         /* filter channels */
02087     filter= (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_LIST_CHANNELS);
02088     ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
02089     
02090         /* get channel from index */
02091     ale= BLI_findlink(&anim_data, channel_index);
02092     if (ale == NULL) {
02093         /* channel not found */
02094         if (G.f & G_DEBUG)
02095             printf("Error: animation channel (index = %d) not found in mouse_anim_channels() \n", channel_index);
02096         
02097         BLI_freelistN(&anim_data);
02098         return 0;
02099     }
02100     
02101     /* selectmode -1 is a special case for ActionGroups only, which selects all of the channels underneath it only... */
02102     // TODO: should this feature be extended to work with other channel types too?
02103     if ((selectmode == -1) && (ale->type != ANIMTYPE_GROUP)) {
02104         /* normal channels should not behave normally in this case */
02105         BLI_freelistN(&anim_data);
02106         return 0;
02107     }
02108     
02109     /* action to take depends on what channel we've got */
02110     // WARNING: must keep this in sync with the equivalent function in nla_channels.c
02111     switch (ale->type) {
02112         case ANIMTYPE_SCENE:
02113         {
02114             Scene *sce= (Scene *)ale->data;
02115             AnimData *adt= sce->adt;
02116             
02117             /* set selection status */
02118             if (selectmode == SELECT_INVERT) {
02119                 /* swap select */
02120                 sce->flag ^= SCE_DS_SELECTED;
02121                 if (adt) adt->flag ^= ADT_UI_SELECTED;
02122             }
02123             else {
02124                 sce->flag |= SCE_DS_SELECTED;
02125                 if (adt) adt->flag |= ADT_UI_SELECTED;
02126             }
02127             
02128             notifierFlags |= (ND_ANIMCHAN|NA_SELECTED);
02129         }
02130             break;
02131         case ANIMTYPE_OBJECT:
02132         {
02133             bDopeSheet *ads= (bDopeSheet *)ac->data;
02134             Scene *sce= (Scene *)ads->source;
02135             Base *base= (Base *)ale->data;
02136             Object *ob= base->object;
02137             AnimData *adt= ob->adt;
02138             
02139             /* set selection status */
02140             if (selectmode == SELECT_INVERT) {
02141                 /* swap select */
02142                 base->flag ^= SELECT;
02143                 ob->flag= base->flag;
02144                 
02145                 if (adt) adt->flag ^= ADT_UI_SELECTED;
02146             }
02147             else {
02148                 Base *b;
02149                 
02150                 /* deselect all */
02151                 // TODO: should this deselect all other types of channels too?
02152                 for (b= sce->base.first; b; b= b->next) {
02153                     b->flag &= ~SELECT;
02154                     b->object->flag= b->flag;
02155                     if (b->object->adt) b->object->adt->flag &= ~(ADT_UI_SELECTED|ADT_UI_ACTIVE);
02156                 }
02157                 
02158                 /* select object now */
02159                 base->flag |= SELECT;
02160                 ob->flag |= SELECT;
02161                 if (adt) adt->flag |= ADT_UI_SELECTED;
02162             }
02163             
02164             if ((adt) && (adt->flag & ADT_UI_SELECTED))
02165                 adt->flag |= ADT_UI_ACTIVE;
02166             
02167             notifierFlags |= (ND_ANIMCHAN|NA_SELECTED);
02168         }
02169             break;
02170         
02171         case ANIMTYPE_FILLACTD: /* Action Expander */
02172         case ANIMTYPE_DSMAT:    /* Datablock AnimData Expanders */
02173         case ANIMTYPE_DSLAM:
02174         case ANIMTYPE_DSCAM:
02175         case ANIMTYPE_DSCUR:
02176         case ANIMTYPE_DSSKEY:
02177         case ANIMTYPE_DSWOR:
02178         case ANIMTYPE_DSPART:
02179         case ANIMTYPE_DSMBALL:
02180         case ANIMTYPE_DSARM:
02181         case ANIMTYPE_DSMESH:
02182         case ANIMTYPE_DSNTREE:
02183         case ANIMTYPE_DSTEX:
02184         case ANIMTYPE_DSLAT:
02185         case ANIMTYPE_DSSPK:
02186         {
02187             /* sanity checking... */
02188             if (ale->adt) {
02189                 /* select/deselect */
02190                 if (selectmode == SELECT_INVERT) {
02191                     /* inverse selection status of this AnimData block only */
02192                     ale->adt->flag ^= ADT_UI_SELECTED;
02193                 }
02194                 else {
02195                     /* select AnimData block by itself */
02196                     ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, 0, ACHANNEL_SETFLAG_CLEAR);
02197                     ale->adt->flag |= ADT_UI_SELECTED;
02198                 }
02199                 
02200                 /* set active? */
02201                 if ((ale->adt) && (ale->adt->flag & ADT_UI_SELECTED))
02202                     ale->adt->flag |= ADT_UI_ACTIVE;
02203             }
02204             
02205             notifierFlags |= (ND_ANIMCHAN|NA_SELECTED);
02206         }   
02207             break;
02208         
02209         case ANIMTYPE_GROUP: 
02210         {
02211             bActionGroup *agrp= (bActionGroup *)ale->data;
02212             
02213             /* select/deselect group */
02214             if (selectmode == SELECT_INVERT) {
02215                 /* inverse selection status of this group only */
02216                 agrp->flag ^= AGRP_SELECTED;
02217             }
02218             else if (selectmode == -1) {
02219                 /* select all in group (and deselect everthing else) */ 
02220                 FCurve *fcu;
02221                 
02222                 /* deselect all other channels */
02223                 ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, 0, ACHANNEL_SETFLAG_CLEAR);
02224                 
02225                 /* only select channels in group and group itself */
02226                 for (fcu= agrp->channels.first; fcu && fcu->grp==agrp; fcu= fcu->next)
02227                     fcu->flag |= FCURVE_SELECTED;
02228                 agrp->flag |= AGRP_SELECTED;                    
02229             }
02230             else {
02231                 /* select group by itself */
02232                 ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, 0, ACHANNEL_SETFLAG_CLEAR);
02233                 agrp->flag |= AGRP_SELECTED;
02234             }
02235             
02236             /* if group is selected now, make group the 'active' one in the visible list */
02237             if (agrp->flag & AGRP_SELECTED)
02238                 ANIM_set_active_channel(ac, ac->data, ac->datatype, filter, agrp, ANIMTYPE_GROUP);
02239                 
02240             notifierFlags |= (ND_ANIMCHAN|NA_SELECTED);
02241         }
02242             break;
02243         case ANIMTYPE_FCURVE: 
02244         {
02245             FCurve *fcu= (FCurve *)ale->data;
02246             
02247             /* select/deselect */
02248             if (selectmode == SELECT_INVERT) {
02249                 /* inverse selection status of this F-Curve only */
02250                 fcu->flag ^= FCURVE_SELECTED;
02251             }
02252             else {
02253                 /* select F-Curve by itself */
02254                 ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, 0, ACHANNEL_SETFLAG_CLEAR);
02255                 fcu->flag |= FCURVE_SELECTED;
02256             }
02257             
02258             /* if F-Curve is selected now, make F-Curve the 'active' one in the visible list */
02259             if (fcu->flag & FCURVE_SELECTED)
02260                 ANIM_set_active_channel(ac, ac->data, ac->datatype, filter, fcu, ANIMTYPE_FCURVE);
02261                 
02262             notifierFlags |= (ND_ANIMCHAN|NA_SELECTED);
02263         }
02264             break;
02265         case ANIMTYPE_SHAPEKEY: 
02266         {
02267             KeyBlock *kb= (KeyBlock *)ale->data;
02268             
02269             /* select/deselect */
02270             if (selectmode == SELECT_INVERT) {
02271                 /* inverse selection status of this ShapeKey only */
02272                 kb->flag ^= KEYBLOCK_SEL;
02273             }
02274             else {
02275                 /* select ShapeKey by itself */
02276                 ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, 0, ACHANNEL_SETFLAG_CLEAR);
02277                 kb->flag |= KEYBLOCK_SEL;
02278             }
02279                 
02280             notifierFlags |= (ND_ANIMCHAN|NA_SELECTED);
02281         }
02282             break;
02283         case ANIMTYPE_GPDATABLOCK:
02284         {
02285             bGPdata *gpd= (bGPdata *)ale->data;
02286             
02287             /* toggle expand 
02288              *  - although the triangle widget already allows this, the whole channel can also be used for this purpose
02289              */
02290             gpd->flag ^= GP_DATA_EXPAND;
02291             
02292             notifierFlags |= (ND_ANIMCHAN|NA_EDITED);
02293         }
02294             break;
02295         case ANIMTYPE_GPLAYER:
02296         {
02297             bGPDlayer *gpl= (bGPDlayer *)ale->data;
02298             
02299             /* select/deselect */
02300             if (selectmode == SELECT_INVERT) {
02301                 /* invert selection status of this layer only */
02302                 gpl->flag ^= GP_LAYER_SELECT;
02303             }
02304             else {  
02305                 /* select layer by itself */
02306                 ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, 0, ACHANNEL_SETFLAG_CLEAR);
02307                 gpl->flag |= GP_LAYER_SELECT;
02308             }
02309             
02310             notifierFlags |= (ND_ANIMCHAN|NA_EDITED);
02311         }
02312             break;
02313         default:
02314             if (G.f & G_DEBUG)
02315                 printf("Error: Invalid channel type in mouse_anim_channels() \n");
02316     }
02317     
02318     /* free channels */
02319     BLI_freelistN(&anim_data);
02320     
02321     /* return notifier flags */
02322     return notifierFlags;
02323 }
02324 
02325 /* ------------------- */
02326 
02327 /* handle clicking */
02328 static int animchannels_mouseclick_invoke(bContext *C, wmOperator *op, wmEvent *event)
02329 {
02330     bAnimContext ac;
02331     ARegion *ar;
02332     View2D *v2d;
02333     int channel_index;
02334     int notifierFlags = 0;
02335     short selectmode;
02336     float x, y;
02337     
02338     
02339     /* get editor data */
02340     if (ANIM_animdata_get_context(C, &ac) == 0)
02341         return OPERATOR_CANCELLED;
02342         
02343     /* get useful pointers from animation context data */
02344     ar= ac.ar;
02345     v2d= &ar->v2d;
02346     
02347     /* select mode is either replace (deselect all, then add) or add/extend */
02348     if (RNA_boolean_get(op->ptr, "extend"))
02349         selectmode= SELECT_INVERT;
02350     else if (RNA_boolean_get(op->ptr, "children_only"))
02351         selectmode= -1; /* this is a bit of a special case for ActionGroups only... should it be removed or extended to all instead? */
02352     else
02353         selectmode= SELECT_REPLACE;
02354     
02355     /* figure out which channel user clicked in 
02356      * Note: although channels technically start at y= ACHANNEL_FIRST, we need to adjust by half a channel's height
02357      *      so that the tops of channels get caught ok. Since ACHANNEL_FIRST is really ACHANNEL_HEIGHT, we simply use
02358      *      ACHANNEL_HEIGHT_HALF.
02359      */
02360     UI_view2d_region_to_view(v2d, event->mval[0], event->mval[1], &x, &y);
02361     UI_view2d_listview_view_to_cell(v2d, ACHANNEL_NAMEWIDTH, ACHANNEL_STEP, 0, (float)ACHANNEL_HEIGHT_HALF, x, y, NULL, &channel_index);
02362     
02363     /* handle mouse-click in the relevant channel then */
02364     notifierFlags= mouse_anim_channels(&ac, x, channel_index, selectmode);
02365     
02366     /* set notifier that things have changed */
02367     WM_event_add_notifier(C, NC_ANIMATION|notifierFlags, NULL);
02368     
02369     return OPERATOR_FINISHED;
02370 }
02371  
02372 static void ANIM_OT_channels_click (wmOperatorType *ot)
02373 {
02374     /* identifiers */
02375     ot->name= "Mouse Click on Channels";
02376     ot->idname= "ANIM_OT_channels_click";
02377     ot->description= "Handle mouse-clicks over animation channels";
02378     
02379     /* api callbacks */
02380     ot->invoke= animchannels_mouseclick_invoke;
02381     ot->poll= animedit_poll_channels_active;
02382     
02383     /* flags */
02384     ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
02385     
02386     /* id-props */
02387     RNA_def_boolean(ot->srna, "extend", 0, "Extend Select", ""); // SHIFTKEY
02388     RNA_def_boolean(ot->srna, "children_only", 0, "Select Children Only", ""); // CTRLKEY|SHIFTKEY
02389 }
02390 
02391 /* ************************************************************************** */
02392 /* Operator Registration */
02393 
02394 void ED_operatortypes_animchannels(void)
02395 {
02396     WM_operatortype_append(ANIM_OT_channels_select_all_toggle);
02397     WM_operatortype_append(ANIM_OT_channels_select_border);
02398     
02399     WM_operatortype_append(ANIM_OT_channels_click);
02400     WM_operatortype_append(ANIM_OT_channels_rename);
02401     
02402     WM_operatortype_append(ANIM_OT_channels_setting_enable);
02403     WM_operatortype_append(ANIM_OT_channels_setting_disable);
02404     WM_operatortype_append(ANIM_OT_channels_setting_invert);
02405     WM_operatortype_append(ANIM_OT_channels_setting_toggle);
02406     
02407     WM_operatortype_append(ANIM_OT_channels_delete);
02408     
02409         // XXX does this need to be a separate operator?
02410     WM_operatortype_append(ANIM_OT_channels_editable_toggle);
02411     
02412     WM_operatortype_append(ANIM_OT_channels_move);
02413     
02414     WM_operatortype_append(ANIM_OT_channels_expand);
02415     WM_operatortype_append(ANIM_OT_channels_collapse);
02416     
02417     WM_operatortype_append(ANIM_OT_channels_visibility_toggle);
02418     WM_operatortype_append(ANIM_OT_channels_visibility_set);
02419     
02420     WM_operatortype_append(ANIM_OT_channels_fcurves_enable);
02421 }
02422 
02423 // TODO: check on a poll callback for this, to get hotkeys into menus
02424 void ED_keymap_animchannels(wmKeyConfig *keyconf)
02425 {
02426     wmKeyMap *keymap = WM_keymap_find(keyconf, "Animation Channels", 0, 0);
02427     wmKeyMapItem *kmi;
02428 
02429     /* selection */
02430         /* click-select */
02431         // XXX for now, only leftmouse.... 
02432     WM_keymap_add_item(keymap, "ANIM_OT_channels_click", LEFTMOUSE, KM_PRESS, 0, 0);
02433     RNA_boolean_set(WM_keymap_add_item(keymap, "ANIM_OT_channels_click", LEFTMOUSE, KM_PRESS, KM_SHIFT, 0)->ptr, "extend", TRUE);
02434     RNA_boolean_set(WM_keymap_add_item(keymap, "ANIM_OT_channels_click", LEFTMOUSE, KM_PRESS, KM_CTRL|KM_SHIFT, 0)->ptr, "children_only", TRUE);
02435     
02436         /* rename */
02437     WM_keymap_add_item(keymap, "ANIM_OT_channels_rename", LEFTMOUSE, KM_PRESS, KM_CTRL, 0);
02438     
02439         /* deselect all */
02440     WM_keymap_add_item(keymap, "ANIM_OT_channels_select_all_toggle", AKEY, KM_PRESS, 0, 0);
02441     RNA_boolean_set(WM_keymap_add_item(keymap, "ANIM_OT_channels_select_all_toggle", IKEY, KM_PRESS, KM_CTRL, 0)->ptr, "invert", TRUE);
02442     
02443         /* borderselect */
02444     WM_keymap_add_item(keymap, "ANIM_OT_channels_select_border", BKEY, KM_PRESS, 0, 0);
02445     WM_keymap_add_item(keymap, "ANIM_OT_channels_select_border", EVT_TWEAK_L, KM_ANY, 0, 0);
02446     
02447     /* delete */
02448     WM_keymap_add_item(keymap, "ANIM_OT_channels_delete", XKEY, KM_PRESS, 0, 0);
02449     WM_keymap_add_item(keymap, "ANIM_OT_channels_delete", DELKEY, KM_PRESS, 0, 0);
02450     
02451     /* settings */
02452     WM_keymap_add_item(keymap, "ANIM_OT_channels_setting_toggle", WKEY, KM_PRESS, KM_SHIFT, 0);
02453     WM_keymap_add_item(keymap, "ANIM_OT_channels_setting_enable", WKEY, KM_PRESS, KM_CTRL|KM_SHIFT, 0);
02454     WM_keymap_add_item(keymap, "ANIM_OT_channels_setting_disable", WKEY, KM_PRESS, KM_ALT, 0);
02455     
02456     /* settings - specialized hotkeys */
02457     WM_keymap_add_item(keymap, "ANIM_OT_channels_editable_toggle", TABKEY, KM_PRESS, 0, 0);
02458     
02459     /* expand/collapse */
02460     WM_keymap_add_item(keymap, "ANIM_OT_channels_expand", PADPLUSKEY, KM_PRESS, 0, 0);
02461     WM_keymap_add_item(keymap, "ANIM_OT_channels_collapse", PADMINUS, KM_PRESS, 0, 0);
02462     
02463     kmi = WM_keymap_add_item(keymap, "ANIM_OT_channels_expand", PADPLUSKEY, KM_PRESS, KM_CTRL, 0);
02464     RNA_boolean_set(kmi->ptr, "all", FALSE);
02465     kmi = WM_keymap_add_item(keymap, "ANIM_OT_channels_collapse", PADMINUS, KM_PRESS, KM_CTRL, 0);
02466     RNA_boolean_set(kmi->ptr, "all", FALSE);
02467 
02468     /* rearranging */
02469     RNA_enum_set(WM_keymap_add_item(keymap, "ANIM_OT_channels_move", PAGEUPKEY, KM_PRESS, 0, 0)->ptr, "direction", REARRANGE_ANIMCHAN_UP);
02470     RNA_enum_set(WM_keymap_add_item(keymap, "ANIM_OT_channels_move", PAGEDOWNKEY, KM_PRESS, 0, 0)->ptr, "direction", REARRANGE_ANIMCHAN_DOWN);
02471     RNA_enum_set(WM_keymap_add_item(keymap, "ANIM_OT_channels_move", PAGEUPKEY, KM_PRESS, KM_SHIFT, 0)->ptr, "direction", REARRANGE_ANIMCHAN_TOP);
02472     RNA_enum_set(WM_keymap_add_item(keymap, "ANIM_OT_channels_move", PAGEDOWNKEY, KM_PRESS, KM_SHIFT, 0)->ptr, "direction", REARRANGE_ANIMCHAN_BOTTOM);
02473     
02474     /* Graph Editor only */
02475     WM_keymap_add_item(keymap, "ANIM_OT_channels_visibility_set", VKEY, KM_PRESS, 0, 0);
02476     WM_keymap_add_item(keymap, "ANIM_OT_channels_visibility_toggle", VKEY, KM_PRESS, KM_SHIFT, 0);
02477 }
02478 
02479 /* ************************************************************************** */