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