Blender  V2.59
nla_channels.c
Go to the documentation of this file.
00001 /*
00002  * $Id: nla_channels.c 36790 2011-05-20 07:40:05Z campbellbarton $
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  * 
00024  * Contributor(s): Joshua Leung (major recode)
00025  *
00026  * ***** END GPL LICENSE BLOCK *****
00027  */
00028 
00034 #include <string.h>
00035 #include <stdio.h>
00036 #include <stdlib.h>
00037 #include <math.h>
00038 
00039 #include "DNA_anim_types.h"
00040 #include "DNA_object_types.h"
00041 #include "DNA_scene_types.h"
00042 
00043 #include "BLI_blenlib.h"
00044 #include "BLI_math.h"
00045 #include "BLI_rand.h"
00046 #include "BLI_utildefines.h"
00047 
00048 #include "BKE_animsys.h"
00049 #include "BKE_nla.h"
00050 #include "BKE_context.h"
00051 #include "BKE_global.h"
00052 #include "BKE_screen.h"
00053 
00054 #include "ED_anim_api.h"
00055 #include "ED_keyframes_edit.h"
00056 #include "ED_screen.h"
00057 
00058 #include "RNA_access.h"
00059 #include "RNA_define.h"
00060 
00061 #include "WM_api.h"
00062 #include "WM_types.h"
00063 
00064 #include "UI_view2d.h"
00065 
00066 #include "nla_intern.h" // own include
00067 
00068 /* *********************************************** */
00069 /* Operators for NLA channels-list which need to be different from the standard Animation Editor ones */
00070 
00071 /* ******************** Mouse-Click Operator *********************** */
00072 /* Depending on the channel that was clicked on, the mouse click will activate whichever
00073  * part of the channel is relevant.
00074  *
00075  * NOTE: eventually, this should probably be phased out when many of these things are replaced with buttons
00076  *      --> Most channels are now selection only...
00077  */
00078 
00079 static int mouse_nla_channels (bAnimContext *ac, float x, int channel_index, short selectmode)
00080 {
00081         ListBase anim_data = {NULL, NULL};
00082         bAnimListElem *ale;
00083         int filter;
00084         View2D *v2d= &ac->ar->v2d;
00085         int notifierFlags = 0;
00086         
00087         /* get the channel that was clicked on */
00088                 /* filter channels */
00089         filter= (ANIMFILTER_VISIBLE | ANIMFILTER_CHANNELS);
00090         filter= ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
00091         
00092                 /* get channel from index */
00093         ale= BLI_findlink(&anim_data, channel_index);
00094         if (ale == NULL) {
00095                 /* channel not found */
00096                 if (G.f & G_DEBUG)
00097                         printf("Error: animation channel (index = %d) not found in mouse_anim_channels() \n", channel_index);
00098                 
00099                 BLI_freelistN(&anim_data);
00100                 return 0;
00101         }
00102         
00103         /* action to take depends on what channel we've got */
00104         // WARNING: must keep this in sync with the equivalent function in anim_channels_edit.c
00105         switch (ale->type) {
00106                 case ANIMTYPE_SCENE:
00107                 {
00108                         Scene *sce= (Scene *)ale->data;
00109                         AnimData *adt= sce->adt;
00110                         
00111                         /* set selection status */
00112                         if (selectmode == SELECT_INVERT) {
00113                                 /* swap select */
00114                                 sce->flag ^= SCE_DS_SELECTED;
00115                                 if (adt) adt->flag ^= ADT_UI_SELECTED;
00116                         }
00117                         else {
00118                                 sce->flag |= SCE_DS_SELECTED;
00119                                 if (adt) adt->flag |= ADT_UI_SELECTED;
00120                         }
00121                         
00122                         notifierFlags |= (ND_ANIMCHAN|NA_SELECTED);
00123                 }
00124                         break;
00125                 case ANIMTYPE_OBJECT:
00126                 {
00127                         bDopeSheet *ads= (bDopeSheet *)ac->data;
00128                         Scene *sce= (Scene *)ads->source;
00129                         Base *base= (Base *)ale->data;
00130                         Object *ob= base->object;
00131                         AnimData *adt= ob->adt;
00132                         
00133                         if (nlaedit_is_tweakmode_on(ac) == 0) {
00134                                 /* set selection status */
00135                                 if (selectmode == SELECT_INVERT) {
00136                                         /* swap select */
00137                                         base->flag ^= SELECT;
00138                                         ob->flag= base->flag;
00139                                         
00140                                         if (adt) adt->flag ^= ADT_UI_SELECTED;
00141                                 }
00142                                 else {
00143                                         Base *b;
00144                                         
00145                                         /* deselect all */
00146                                         // TODO: should this deselect all other types of channels too?
00147                                         for (b= sce->base.first; b; b= b->next) {
00148                                                 b->flag &= ~SELECT;
00149                                                 b->object->flag= b->flag;
00150                                                 if (b->object->adt) b->object->adt->flag &= ~(ADT_UI_SELECTED|ADT_UI_ACTIVE);
00151                                         }
00152                                         
00153                                         /* select object now */
00154                                         base->flag |= SELECT;
00155                                         ob->flag |= SELECT;
00156                                         if (adt) adt->flag |= ADT_UI_SELECTED;
00157                                 }
00158                                 
00159                                 if ((adt) && (adt->flag & ADT_UI_SELECTED))
00160                                         adt->flag |= ADT_UI_ACTIVE;
00161                                 
00162                                 /* notifiers - channel was selected */
00163                                 notifierFlags |= (ND_ANIMCHAN|NA_SELECTED);
00164                         }
00165                 }
00166                         break;
00167                         
00168                 case ANIMTYPE_FILLACTD: /* Action Expander */
00169                 case ANIMTYPE_DSMAT:    /* Datablock AnimData Expanders */
00170                 case ANIMTYPE_DSLAM:
00171                 case ANIMTYPE_DSCAM:
00172                 case ANIMTYPE_DSCUR:
00173                 case ANIMTYPE_DSSKEY:
00174                 case ANIMTYPE_DSWOR:
00175                 case ANIMTYPE_DSNTREE:
00176                 case ANIMTYPE_DSPART:
00177                 case ANIMTYPE_DSMBALL:
00178                 case ANIMTYPE_DSARM:
00179                 case ANIMTYPE_DSMESH:
00180                 case ANIMTYPE_DSTEX:
00181                 case ANIMTYPE_DSLAT:
00182                 {
00183                         /* sanity checking... */
00184                         if (ale->adt) {
00185                                 /* select/deselect */
00186                                 if (selectmode == SELECT_INVERT) {
00187                                         /* inverse selection status of this AnimData block only */
00188                                         ale->adt->flag ^= ADT_UI_SELECTED;
00189                                 }
00190                                 else {
00191                                         /* select AnimData block by itself */
00192                                         ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, 0, ACHANNEL_SETFLAG_CLEAR);
00193                                         ale->adt->flag |= ADT_UI_SELECTED;
00194                                 }
00195                                 
00196                                 /* set active? */
00197                                 if ((ale->adt) && (ale->adt->flag & ADT_UI_SELECTED))
00198                                         ale->adt->flag |= ADT_UI_ACTIVE;
00199                         }
00200                         
00201                         notifierFlags |= (ND_ANIMCHAN|NA_SELECTED);
00202                 }       
00203                         break;
00204                         
00205                 case ANIMTYPE_NLATRACK:
00206                 {
00207                         NlaTrack *nlt= (NlaTrack *)ale->data;
00208                         AnimData *adt= ale->adt;
00209                         short offset;
00210                         
00211                         /* offset for start of channel (on LHS of channel-list) */
00212                         if (ale->id) {
00213                                 /* special exception for materials and particles */
00214                                 if (ELEM(GS(ale->id->name),ID_MA,ID_PA))
00215                                         offset= 21 + NLACHANNEL_BUTTON_WIDTH;
00216                                 else
00217                                         offset= 14;
00218                         }
00219                         else
00220                                 offset= 0;
00221                         
00222                         if (x >= (v2d->cur.xmax-NLACHANNEL_BUTTON_WIDTH)) {
00223                                 /* toggle protection (only if there's a toggle there) */
00224                                 nlt->flag ^= NLATRACK_PROTECTED;
00225                                 
00226                                 /* notifier flags - channel was edited */
00227                                 notifierFlags |= (ND_ANIMCHAN|NA_EDITED);
00228                         }
00229                         else if (x >= (v2d->cur.xmax-2*NLACHANNEL_BUTTON_WIDTH)) {
00230                                 /* toggle mute */
00231                                 nlt->flag ^= NLATRACK_MUTED;
00232                                 
00233                                 /* notifier flags - channel was edited */
00234                                 notifierFlags |= (ND_ANIMCHAN|NA_EDITED);
00235                         }
00236                         else if (x <= ((NLACHANNEL_BUTTON_WIDTH*2)+offset)) {
00237                                 /* toggle 'solo' */
00238                                 BKE_nlatrack_solo_toggle(adt, nlt);
00239                                 
00240                                 /* notifier flags - channel was edited */
00241                                 notifierFlags |= (ND_ANIMCHAN|NA_EDITED);
00242                         }
00243                         else if (nlaedit_is_tweakmode_on(ac) == 0) {
00244                                 /* set selection */
00245                                 if (selectmode == SELECT_INVERT) {
00246                                         /* inverse selection status of this F-Curve only */
00247                                         nlt->flag ^= NLATRACK_SELECTED;
00248                                 }
00249                                 else {
00250                                         /* select F-Curve by itself */
00251                                         ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, 0, ACHANNEL_SETFLAG_CLEAR);
00252                                         nlt->flag |= NLATRACK_SELECTED;
00253                                 }
00254                                 
00255                                 /* if NLA-Track is selected now, make NLA-Track the 'active' one in the visible list */
00256                                 if (nlt->flag & NLATRACK_SELECTED)
00257                                         ANIM_set_active_channel(ac, ac->data, ac->datatype, filter, nlt, ANIMTYPE_NLATRACK);
00258                                         
00259                                 /* notifier flags - channel was selected */
00260                                 notifierFlags |= (ND_ANIMCHAN|NA_SELECTED);
00261                         }
00262                 }
00263                         break;
00264                 case ANIMTYPE_NLAACTION:
00265                 {
00266                         AnimData *adt= BKE_animdata_from_id(ale->id);
00267                         
00268                         if (x >= (v2d->cur.xmax-NLACHANNEL_BUTTON_WIDTH)) {
00269                                 if (nlaedit_is_tweakmode_on(ac) == 0) {
00270                                         /* 'push-down' action - only usable when not in TweakMode */
00271                                         // TODO: make this use the operator instead of calling the function directly
00272                                         //      however, calling the operator requires that we supply the args, and that works with proper buttons only
00273                                         BKE_nla_action_pushdown(adt);
00274                                 }
00275                                 else {
00276                                         /* when in tweakmode, this button becomes the toggle for mapped editing */
00277                                         adt->flag ^= ADT_NLA_EDIT_NOMAP;
00278                                 }
00279                                 
00280                                 /* changes to NLA-Action occurred */
00281                                 notifierFlags |= ND_NLA_ACTCHANGE;
00282                         }
00283                 }
00284                         break;
00285                         
00286                 default:
00287                         if (G.f & G_DEBUG)
00288                                 printf("Error: Invalid channel type in mouse_nla_channels() \n");
00289         }
00290         
00291         /* free channels */
00292         BLI_freelistN(&anim_data);
00293         
00294         /* return the notifier-flags set */
00295         return notifierFlags;
00296 }
00297 
00298 /* ------------------- */
00299 
00300 /* handle clicking */
00301 static int nlachannels_mouseclick_invoke(bContext *C, wmOperator *op, wmEvent *event)
00302 {
00303         bAnimContext ac;
00304         ARegion *ar;
00305         View2D *v2d;
00306         int channel_index;
00307         int notifierFlags = 0;
00308         short selectmode;
00309         float x, y;
00310         
00311         /* get editor data */
00312         if (ANIM_animdata_get_context(C, &ac) == 0)
00313                 return OPERATOR_CANCELLED;
00314                 
00315         /* get useful pointers from animation context data */
00316         ar= ac.ar;
00317         v2d= &ar->v2d;
00318         
00319         /* select mode is either replace (deselect all, then add) or add/extend */
00320         if (RNA_boolean_get(op->ptr, "extend"))
00321                 selectmode= SELECT_INVERT;
00322         else
00323                 selectmode= SELECT_REPLACE;
00324         
00325         /* figure out which channel user clicked in 
00326          * Note: although channels technically start at y= NLACHANNEL_FIRST, we need to adjust by half a channel's height
00327          *              so that the tops of channels get caught ok. Since NLACHANNEL_FIRST is really NLACHANNEL_HEIGHT, we simply use
00328          *              NLACHANNEL_HEIGHT_HALF.
00329          */
00330         UI_view2d_region_to_view(v2d, event->mval[0], event->mval[1], &x, &y);
00331         UI_view2d_listview_view_to_cell(v2d, NLACHANNEL_NAMEWIDTH, NLACHANNEL_STEP, 0, (float)NLACHANNEL_HEIGHT_HALF, x, y, NULL, &channel_index);
00332         
00333         /* handle mouse-click in the relevant channel then */
00334         notifierFlags= mouse_nla_channels(&ac, x, channel_index, selectmode);
00335         
00336         /* set notifier that things have changed */
00337         WM_event_add_notifier(C, NC_ANIMATION|notifierFlags, NULL);
00338         
00339         return OPERATOR_FINISHED;
00340 }
00341  
00342 void NLA_OT_channels_click (wmOperatorType *ot)
00343 {
00344         /* identifiers */
00345         ot->name= "Mouse Click on NLA Channels";
00346         ot->idname= "NLA_OT_channels_click";
00347         ot->description= "Handle clicks to select NLA channels";
00348         
00349         /* api callbacks */
00350         ot->invoke= nlachannels_mouseclick_invoke;
00351         ot->poll= ED_operator_nla_active;
00352         
00353         /* flags */
00354         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
00355         
00356         /* id-props */
00357         RNA_def_boolean(ot->srna, "extend", 0, "Extend Select", ""); // SHIFTKEY
00358 }
00359 
00360 /* *********************************************** */
00361 /* Special Operators */
00362 
00363 /* ******************** Add Tracks Operator ***************************** */
00364 /* Add NLA Tracks to the same AnimData block as a selected track, or above the selected tracks */
00365 
00366 static int nlaedit_add_tracks_exec (bContext *C, wmOperator *op)
00367 {
00368         bAnimContext ac;
00369         
00370         ListBase anim_data = {NULL, NULL};
00371         bAnimListElem *ale;
00372         int filter;
00373         
00374         AnimData *lastAdt = NULL;
00375         short above_sel= RNA_boolean_get(op->ptr, "above_selected");
00376         
00377         /* get editor data */
00378         if (ANIM_animdata_get_context(C, &ac) == 0)
00379                 return OPERATOR_CANCELLED;
00380                 
00381         /* get a list of the AnimData blocks being shown in the NLA */
00382         filter= (ANIMFILTER_VISIBLE | ANIMFILTER_NLATRACKS | ANIMFILTER_SEL);
00383         ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
00384         
00385         /* add tracks... */
00386         for (ale= anim_data.first; ale; ale= ale->next) {
00387                 if(ale->type == ANIMTYPE_NLATRACK) {
00388                         NlaTrack *nlt= (NlaTrack *)ale->data;
00389                         AnimData *adt= ale->adt;
00390                         
00391                         /* check if just adding a new track above this one,
00392                          * or whether we're adding a new one to the top of the stack that this one belongs to
00393                          */
00394                         if (above_sel) {
00395                                 /* just add a new one above this one */
00396                                 add_nlatrack(adt, nlt);
00397                         }
00398                         else if ((lastAdt == NULL) || (adt != lastAdt)) {
00399                                 /* add one track to the top of the owning AnimData's stack, then don't add anymore to this stack */
00400                                 add_nlatrack(adt, NULL);
00401                                 lastAdt= adt;
00402                         }
00403                 }
00404         }
00405         
00406         /* free temp data */
00407         BLI_freelistN(&anim_data);
00408         
00409         /* set notifier that things have changed */
00410         WM_event_add_notifier(C, NC_ANIMATION|ND_NLA|NA_EDITED, NULL);
00411         
00412         /* done */
00413         return OPERATOR_FINISHED;
00414 }
00415 
00416 void NLA_OT_tracks_add (wmOperatorType *ot)
00417 {
00418         /* identifiers */
00419         ot->name= "Add Track(s)";
00420         ot->idname= "NLA_OT_tracks_add";
00421         ot->description= "Add NLA-Tracks above/after the selected tracks";
00422         
00423         /* api callbacks */
00424         ot->exec= nlaedit_add_tracks_exec;
00425         ot->poll= nlaop_poll_tweakmode_off;
00426         
00427         /* flags */
00428         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
00429         
00430         /* properties */
00431         RNA_def_boolean(ot->srna, "above_selected", 0, "Above Selected", "Add a new NLA Track above every existing selected one.");
00432 }
00433 
00434 /* ******************** Delete Tracks Operator ***************************** */
00435 /* Delete selected NLA Tracks */
00436 
00437 static int nlaedit_delete_tracks_exec (bContext *C, wmOperator *UNUSED(op))
00438 {
00439         bAnimContext ac;
00440         
00441         ListBase anim_data = {NULL, NULL};
00442         bAnimListElem *ale;
00443         int filter;
00444         
00445         /* get editor data */
00446         if (ANIM_animdata_get_context(C, &ac) == 0)
00447                 return OPERATOR_CANCELLED;
00448                 
00449         /* get a list of the AnimData blocks being shown in the NLA */
00450         filter= (ANIMFILTER_VISIBLE | ANIMFILTER_NLATRACKS | ANIMFILTER_SEL);
00451         ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
00452         
00453         /* delete tracks */
00454         for (ale= anim_data.first; ale; ale= ale->next) {
00455                 if(ale->type == ANIMTYPE_NLATRACK) {
00456                         NlaTrack *nlt= (NlaTrack *)ale->data;
00457                         AnimData *adt= ale->adt;
00458                         
00459                         /* if track is currently 'solo', then AnimData should have its
00460                          * 'has solo' flag disabled
00461                          */
00462                         if (nlt->flag & NLATRACK_SOLO)
00463                                 adt->flag &= ~ADT_NLA_SOLO_TRACK;
00464                         
00465                         /* call delete on this track - deletes all strips too */
00466                         free_nlatrack(&adt->nla_tracks, nlt);
00467                 }
00468         }
00469         
00470         /* free temp data */
00471         BLI_freelistN(&anim_data);
00472         
00473         /* set notifier that things have changed */
00474         WM_event_add_notifier(C, NC_ANIMATION|ND_NLA|NA_EDITED, NULL);
00475         
00476         /* done */
00477         return OPERATOR_FINISHED;
00478 }
00479 
00480 void NLA_OT_delete_tracks (wmOperatorType *ot)
00481 {
00482         /* identifiers */
00483         ot->name= "Delete Tracks";
00484         ot->idname= "NLA_OT_delete_tracks";
00485         ot->description= "Delete selected NLA-Tracks and the strips they contain";
00486         
00487         /* api callbacks */
00488         ot->exec= nlaedit_delete_tracks_exec;
00489         ot->poll= nlaop_poll_tweakmode_off;
00490         
00491         /* flags */
00492         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
00493 }
00494 
00495 /* *********************************************** */