/** * $Id$ * * ***** BEGIN GPL/BL DUAL LICENSE BLOCK ***** * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. The Blender * Foundation also sells licenses for use in proprietary software under * the Blender License. See http://www.blender.org/BL/ for information * about this. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. * All rights reserved. * * The Original Code is: all of this file. * * Contributor(s): none yet. * * ***** END GPL/BL DUAL LICENSE BLOCK ***** * This file is a horrible mess: An attmept to cram some * final functionality into blender before it is too late. * * Hopefully it can be tidied up at a later date... */ #include #include #include #include "PIL_time.h" #include "BKE_action.h" #include "BKE_global.h" #include "BKE_ipo.h" #include "BKE_library.h" #include "BKE_main.h" #include "BKE_nla.h" #include "MEM_guardedalloc.h" #include "BLI_blenlib.h" #include "DNA_screen_types.h" #include "DNA_space_types.h" #include "DNA_scene_types.h" #include "DNA_ipo_types.h" #include "DNA_curve_types.h" #include "DNA_object_types.h" #include "DNA_userdef_types.h" #include "DNA_action_types.h" #include "DNA_nla_types.h" #include "DNA_constraint_types.h" #include "BIF_screen.h" #include "BIF_interface.h" #include "BIF_butspace.h" #include "BIF_space.h" #include "BIF_mywindow.h" #include "BIF_editview.h" #include "BIF_toolbox.h" #include "BIF_editnla.h" #include "BSE_editipo.h" #include "BSE_editnla_types.h" #include "BSE_headerbuttons.h" #include "BSE_drawipo.h" #include "BSE_trans_types.h" #include "BSE_edit.h" #include "BSE_filesel.h" #include "BDR_editobject.h" #include "BSE_drawnla.h" #include "blendef.h" #include "mydevice.h" #ifdef HAVE_CONFIG_H #include #endif /* Note: A lot of these pretty much duplicate the behaviour of the action windows. The functions should be shared, not copy-pasted */ static void mouse_nla(int selectmode); static Base *get_nearest_nlachannel_ob_key (float *index, short *sel); static bAction *get_nearest_nlachannel_ac_key (float *index, short *sel); static Base *get_nearest_nlastrip (bActionStrip **rstrip, short *sel); static void mouse_nlachannels(short mval[2]); static void add_nlablock(short mval[2]); static void convert_nla(short mval[2]); extern int count_nla_levels(void); /* From drawnla.c */ extern int nla_filter (Base* base, int flags); /* From drawnla.c */ /* ******************** SPACE: NLA ********************** */ void shift_nlastrips_up(void) { Base *base; bActionStrip *strip, *prevstrip; for (base=G.scene->base.first; base; base=base->next) { if (base->object->type == OB_ARMATURE) { for (strip = base->object->nlastrips.first; strip; strip=strip->next){ if (strip->flag & ACTSTRIP_SELECT) { if ( (prevstrip = strip->prev) ) { if (prevstrip->prev) prevstrip->prev->next = strip; if (strip->next) strip->next->prev = prevstrip; strip->prev = prevstrip->prev; prevstrip->next = strip->next; strip->next = prevstrip; prevstrip->prev = strip; if (prevstrip == base->object->nlastrips.first) base->object->nlastrips.first = strip; if (strip == base->object->nlastrips.last) base->object->nlastrips.last = prevstrip; strip = prevstrip; } else { break; } } } } } BIF_undo_push("Shift NLA strip"); allqueue (REDRAWNLA, 0); } void shift_nlastrips_down(void) { Base *base; bActionStrip *strip, *nextstrip; for (base=G.scene->base.first; base; base=base->next) { if (base->object->type == OB_ARMATURE) { for (strip = base->object->nlastrips.last; strip; strip=strip->prev){ if (strip->flag & ACTSTRIP_SELECT) { if ( (nextstrip = strip->next) ) { if (nextstrip->next) nextstrip->next->prev = strip; if (strip->prev) strip->prev->next = nextstrip; strip->next = nextstrip->next; nextstrip->prev = strip->prev; strip->prev = nextstrip; nextstrip->next = strip; if (nextstrip == base->object->nlastrips.last) base->object->nlastrips.last = strip; if (strip == base->object->nlastrips.first) base->object->nlastrips.first = nextstrip; strip = nextstrip; } else { break; } } } } } BIF_undo_push("Shift NLA strips"); allqueue (REDRAWNLA, 0); } void winqreadnlaspace(ScrArea *sa, void *spacedata, BWinEvent *evt) { unsigned short event= evt->event; short val= evt->val; SpaceNla *snla = curarea->spacedata.first; int doredraw= 0; short mval[2]; float dx,dy; int cfra; short mousebut = L_MOUSE; if (curarea->win==0) return; if (!snla) return; if(val) { if( uiDoBlocks(&curarea->uiblocks, event)!=UI_NOTHING ) event= 0; /* swap mouse buttons based on user preference */ if (U.flag & USER_LMOUSESELECT) { if (event == LEFTMOUSE) { event = RIGHTMOUSE; mousebut = L_MOUSE; } else if (event == RIGHTMOUSE) { event = LEFTMOUSE; mousebut = R_MOUSE; } } getmouseco_areawin(mval); switch(event) { case UI_BUT_EVENT: do_nlabuts(val); // in drawnla.c break; case HOMEKEY: do_nla_buttons(B_NLAHOME); break; case EQUALKEY: case PAGEUPKEY: shift_nlastrips_up(); break; case MINUSKEY: case PAGEDOWNKEY: shift_nlastrips_down(); break; case AKEY: if (G.qual & LR_SHIFTKEY){ add_nlablock(mval); allqueue (REDRAWNLA, 0); allqueue (REDRAWVIEW3D, 0); } else{ if (mval[0]>=NLAWIDTH) deselect_nlachannel_keys(1); else{ deselect_nlachannels(1); allqueue (REDRAWVIEW3D, 0); } allqueue (REDRAWNLA, 0); allqueue (REDRAWIPO, 0); BIF_undo_push("(De)select all NLA"); } break; case BKEY: borderselect_nla(); break; case CKEY: convert_nla(mval); break; case DKEY: if (G.qual & LR_SHIFTKEY && mval[0]>=NLAWIDTH){ duplicate_nlachannel_keys(); update_for_newframe_muted(); } break; case GKEY: if (mval[0]>=NLAWIDTH) transform_nlachannel_keys ('g'); update_for_newframe_muted(); break; case NKEY: if(G.qual==0) { toggle_blockhandler(curarea, NLA_HANDLER_PROPERTIES, UI_PNL_TO_MOUSE); scrarea_queue_winredraw(curarea); } break; case SKEY: if (mval[0]>=NLAWIDTH) transform_nlachannel_keys ('s'); update_for_newframe_muted(); break; case DELKEY: case XKEY: if (mval[0]>=NLAWIDTH) delete_nlachannel_keys (); else delete_nlachannels(); update_for_newframe_muted(); break; /* LEFTMOUSE and RIGHTMOUSE event codes can be swapped above, * based on user preference USER_LMOUSESELECT */ case LEFTMOUSE: if(view2dmove(LEFTMOUSE)); // only checks for sliders else if (mval[0]>NLAWIDTH){ do { getmouseco_areawin(mval); areamouseco_to_ipoco(G.v2d, mval, &dx, &dy); cfra= (int)dx; if(cfra< 1) cfra= 1; if( cfra!=CFRA ) { CFRA= cfra; update_for_newframe(); force_draw_plus(SPACE_VIEW3D, 1); force_draw_plus(SPACE_IPO, 1); } else PIL_sleep_ms(30); } while(get_mbut() & mousebut); } break; case RIGHTMOUSE: if (mval[0]>=NLAWIDTH) { if(G.qual & LR_SHIFTKEY) mouse_nla(SELECT_INVERT); else mouse_nla(SELECT_REPLACE); } else mouse_nlachannels(mval); break; case PADPLUSKEY: view2d_zoom(G.v2d, 0.1154, sa->winx, sa->winy); test_view2d(G.v2d, sa->winx, sa->winy); doredraw= 1; break; case PADMINUS: view2d_zoom(G.v2d, -0.15, sa->winx, sa->winy); test_view2d(G.v2d, sa->winx, sa->winy); doredraw= 1; break; case MIDDLEMOUSE: case WHEELUPMOUSE: case WHEELDOWNMOUSE: view2dmove(event); /* in drawipo.c */ break; } } if(doredraw) scrarea_queue_winredraw(curarea); } static void convert_nla(short mval[2]) { short event; float ymax, ymin; Base *base; float x,y; int sel=0; bActionStrip *strip, *nstrip; /* Find out what strip we're over */ ymax = count_nla_levels() * (NLACHANNELSKIP+NLACHANNELHEIGHT); ymax+= NLACHANNELHEIGHT/2; areamouseco_to_ipoco(G.v2d, mval, &x, &y); for (base=G.scene->base.first; base; base=base->next){ if (nla_filter(base, 0)){ /* Check object ipo */ ymin=ymax-(NLACHANNELSKIP+NLACHANNELHEIGHT); if (y>=ymin && y<=ymax) break; ymax=ymin; if (base->object->type==OB_ARMATURE){ /* Check action ipo */ ymin=ymax-(NLACHANNELSKIP+NLACHANNELHEIGHT); if (y>=ymin && y<=ymax) break; ymax=ymin; /* Check nlastrips */ for (strip=base->object->nlastrips.first; strip; strip=strip->next){ ymin=ymax-(NLACHANNELSKIP+NLACHANNELHEIGHT); if (y>=ymin && y<=ymax){ sel = 1; break; } ymax=ymin; } if (sel) break; } } } if (!base) return; if (base->object->type==OB_ARMATURE){ event = pupmenu("Convert%t|Action to NLA Strip%x1"); switch (event){ case 1: if (base->object->action){ /* Make new actionstrip */ nstrip = MEM_callocN(sizeof(bActionStrip), "bActionStrip"); deselect_nlachannel_keys(0); /* Link the action to the nstrip */ nstrip->act = base->object->action; nstrip->actstart = calc_action_start(base->object->action); /* MAKE THIS THE FIRST FRAME OF THE ACTION */ nstrip->actend = calc_action_end(base->object->action); nstrip->start = nstrip->actstart; nstrip->end = nstrip->actend; nstrip->flag = ACTSTRIP_SELECT; nstrip->repeat = 1.0; BLI_addtail(&base->object->nlastrips, nstrip); /* Unlink action */ base->object->action = NULL; BIF_undo_push("Convert NLA"); allqueue (REDRAWNLA, 0); } break; default: break; } } } static Base *nla_base=NULL; /* global, bad, bad! put it in nla space later, or recode the 2 functions below (ton) */ static void add_nla_block(short event) { bAction *act=NULL; bActionStrip *strip; int cur; if (event!=-1){ for (cur = 1, act=G.main->action.first; act; act=act->id.next, cur++){ if (cur==event){ break; } } } /* Bail out if no action was chosen */ if (!act){ return; } /* Initialize the new action block */ strip = MEM_callocN(sizeof(bActionStrip), "bActionStrip"); deselect_nlachannel_keys(0); /* Link the action to the strip */ strip->act = act; strip->actstart = 1.0; strip->actend = calc_action_end(act); strip->start = G.scene->r.cfra; /* Should be mval[0] */ strip->end = strip->start + (strip->actend-strip->actstart); strip->flag = ACTSTRIP_SELECT; strip->repeat = 1.0; act->id.us++; BLI_addtail(&nla_base->object->nlastrips, strip); BIF_undo_push("Add NLA strip"); } static void add_nla_databrowse_callback(unsigned short val) { /* val is not used, databrowse needs it to optional pass an event */ short event; if(nla_base==NULL) return; event= G.snla->menunr; /* set by databrowse or pupmenu */ add_nla_block(event); } static void add_nlablock(short mval[2]) { /* Make sure we are over an armature */ Base *base; float ymin, ymax; float x, y; rctf rectf; short event; char *str; short nr; bConstraintChannel *conchan=NULL; areamouseco_to_ipoco(G.v2d, mval, &x, &y); mval[0]-=7; areamouseco_to_ipoco(G.v2d, mval, &rectf.xmin, &rectf.ymin); mval[0]+=14; areamouseco_to_ipoco(G.v2d, mval, &rectf.xmax, &rectf.ymax); ymax = count_nla_levels(); ymax*= (NLACHANNELHEIGHT + NLACHANNELSKIP); ymax+= NLACHANNELHEIGHT/2; for (base=G.scene->base.first; base; base=base->next){ /* Handle object ipo selection */ if (nla_filter(base, 0)){ /* Area that encloses object name (or ipo) */ ymin=ymax-(NLACHANNELHEIGHT+NLACHANNELSKIP); /* Area that encloses constraint channels */ for (conchan=base->object->constraintChannels.first; conchan; conchan=conchan->next){ ymin-=(NLACHANNELHEIGHT+NLACHANNELSKIP); } if (base->object->type==OB_ARMATURE){ /* Area that encloses selected action, if * present */ if (base->object->action) ymin-=(NLACHANNELHEIGHT+NLACHANNELSKIP); /* Area that encloses nla strips */ ymin-=(NLACHANNELHEIGHT+NLACHANNELSKIP)* (BLI_countlist(&base->object->nlastrips)); } /* Test to see the mouse is in an armature area */ if (base->object->type==OB_ARMATURE){ if (!((ymax < rectf.ymin) || (ymin > rectf.ymax))) break; } ymax=ymin; } } /* global... for the call above, because the NLA system seems not to have an 'active strip' stored */ nla_base= base; /* Make sure we have an armature */ if (!base){ error ("Not an armature"); return; } /* Popup action menu */ IDnames_to_pupstring(&str, "Add Action", NULL, &G.main->action, (ID *)G.scene, &nr); if(strncmp(str+13, "DataBrow", 8)==0) { MEM_freeN(str); activate_databrowse((ID *)NULL, ID_AC, 0, 0, &G.snla->menunr, add_nla_databrowse_callback ); return; } else { event = pupmenu(str); MEM_freeN(str); add_nla_block(event); } /* Ton: this is a callback for databrowse too Hos: no, I don't think it is add_nla_block(0); */ } static void mouse_nlachannels(short mval[2]) { /* Find which strip has been clicked */ // bActionChannel *chan; bConstraintChannel *conchan=NULL; bActionStrip *strip; float click, x,y; int wsize; int sel; Base *base; wsize = (count_nla_levels ()*(NLACHANNELHEIGHT+NLACHANNELSKIP)); wsize+= NLACHANNELHEIGHT/2; areamouseco_to_ipoco(G.v2d, mval, &x, &y); click = ((wsize - y) / (NLACHANNELHEIGHT+NLACHANNELSKIP)); if (click<0) return; for (base = G.scene->base.first; base; base=base->next){ if (nla_filter(base, 0)) { /* See if this is a base selected */ if ((int)click==0) break; click--; /* Check for click in a constraint */ for (conchan=base->object->constraintChannels.first; conchan; conchan=conchan->next){ if ((int)click==0){ base=G.scene->base.last; break; } click--; } /* See if this is an action */ if (base->object->type==OB_ARMATURE && base->object->action){ if ((int)click==0){ break; } click--; } /* See if this is an nla strip */ for (strip = base->object->nlastrips.first; strip; strip=strip->next){ if ((int)click==0){ base=G.scene->base.last; break; } click--; } } } if (!base && !conchan) return; /* Handle constraint strip selection */ if (conchan){ if (conchan->flag & CONSTRAINT_CHANNEL_SELECT) sel = 0; else sel =1; /* Channel names clicking */ if (G.qual & LR_SHIFTKEY){ // select_poseelement_by_name(chan->name, !(chan->flag & ACHAN_SELECTED)); if (conchan->flag & CONSTRAINT_CHANNEL_SELECT){ conchan->flag &= ~CONSTRAINT_CHANNEL_SELECT; // hilight_channel(act, chan, 0); } else{ conchan->flag |= CONSTRAINT_CHANNEL_SELECT; // hilight_channel(act, chan, 1); } } else{ deselect_nlachannels (0); // Auto clear conchan->flag |= CONSTRAINT_CHANNEL_SELECT; // hilight_channel(act, chan, 1); // act->achan = chan; // select_poseelement_by_name(chan->name, 1); } } /* Handle object strip selection */ else if (base) { /* Choose the mode */ if (base->flag & SELECT) sel = 0; else sel =1; /* Channel names clicking */ if (G.qual & LR_SHIFTKEY) { // select_poseelement_by_name(chan->name, !(chan->flag & ACHAN_SELECTED)); if (base->flag & SELECT) { base->flag &= ~SELECT; // hilight_channel(act, chan, 0); } else { base->flag |= SELECT; // hilight_channel(act, chan, 1); } } else { deselect_nlachannels (0); // Auto clear base->flag |= SELECT; // hilight_channel(act, chan, 1); // act->achan = chan; // select_poseelement_by_name(chan->name, 1); } } allqueue (REDRAWIPO, 0); allqueue (REDRAWVIEW3D, 0); allqueue (REDRAWACTION, 0); allqueue(REDRAWNLA, 0); } void deselect_nlachannel_keys (int test) { Base *base; int sel=1; bActionChannel *chan; bActionStrip *strip; bConstraintChannel *conchan; /* Determine if this is selection or deselection */ if (test){ for (base=G.scene->base.first; base && sel; base=base->next){ /* Test object ipos */ if (is_ipo_key_selected(base->object->ipo)){ sel = 0; break; } /* Test object constraint ipos */ if (sel){ for (conchan=base->object->constraintChannels.first; conchan; conchan=conchan->next){ if (is_ipo_key_selected(conchan->ipo)){ sel=0; break; } } } /* Test action ipos */ if (sel){ if (base->object->type==OB_ARMATURE && base->object->action){ for (chan=base->object->action->chanbase.first; chan; chan=chan->next){ if (is_ipo_key_selected(chan->ipo)){ sel=0; break; } /* Test action constraints */ if (sel){ for (conchan=chan->constraintChannels.first; conchan; conchan=conchan->next){ if (is_ipo_key_selected(conchan->ipo)){ sel=0; break; } } } } } } /* Test NLA strips */ if (sel){ if (base->object->type==OB_ARMATURE){ for (strip=base->object->nlastrips.first; strip; strip=strip->next){ if (strip->flag & ACTSTRIP_SELECT){ sel = 0; break; } } } } } } else sel=0; /* Set the flags */ for (base=G.scene->base.first; base; base=base->next){ /* Set the object ipos */ set_ipo_key_selection(base->object->ipo, sel); /* Set the object constraint ipos */ for (conchan=base->object->constraintChannels.first; conchan; conchan=conchan->next){ set_ipo_key_selection(conchan->ipo, sel); } /* Set the action ipos */ if (base->object->type==OB_ARMATURE && base->object->action){ for (chan=base->object->action->chanbase.first; chan; chan=chan->next){ set_ipo_key_selection(chan->ipo, sel); /* Set the action constraint ipos */ for (conchan=chan->constraintChannels.first; conchan; conchan=conchan->next) set_ipo_key_selection(conchan->ipo, sel); } } /* Set the nlastrips */ if (base->object->type==OB_ARMATURE){ for (strip=base->object->nlastrips.first; strip; strip=strip->next){ if (sel) strip->flag |= ACTSTRIP_SELECT; else strip->flag &= ~ACTSTRIP_SELECT; } } } } /* very bad call! */ static void recalc_all_ipos(void) { Ipo *ipo; IpoCurve *icu; /* Go to each ipo */ for (ipo=G.main->ipo.first; ipo; ipo=ipo->id.next){ for (icu = ipo->curve.first; icu; icu=icu->next){ sort_time_ipocurve(icu); testhandles_ipocurve(icu); } } } void transform_nlachannel_keys(char mode) { Base *base; TransVert *tv; int /*sel=0,*/ i; short mvals[2], mvalc[2]; // short cent[2]; float sval[2], cval[2], lastcval[2]; short cancel=0; float fac=0.0F; int loop=1; int tvtot=0; float deltax, startx; // float cenf[2]; int invert=0, firsttime=1; char str[256]; bActionChannel *chan; bActionStrip *strip; bConstraintChannel *conchan; /* Ensure that partial selections result in beztriple selections */ for (base=G.scene->base.first; base; base=base->next){ /* Check object ipos */ tvtot+=fullselect_ipo_keys(base->object->ipo); /* Check object constraint ipos */ for(conchan=base->object->constraintChannels.first; conchan; conchan=conchan->next) tvtot+=fullselect_ipo_keys(conchan->ipo); /* Check action ipos */ if (base->object->type == OB_ARMATURE && base->object->action){ for (chan=base->object->action->chanbase.first; chan; chan=chan->next){ tvtot+=fullselect_ipo_keys(chan->ipo); /* Check action constraint ipos */ for (conchan=chan->constraintChannels.first; conchan; conchan=conchan->next) tvtot+=fullselect_ipo_keys(conchan->ipo); } } /* Check nlastrips */ if (base->object->type==OB_ARMATURE){ for (strip=base->object->nlastrips.first; strip; strip=strip->next){ if (strip->flag & ACTSTRIP_SELECT) tvtot+=2; } } } /* If nothing is selected, bail out */ if (!tvtot) return; /* Build the transvert structure */ tv = MEM_callocN (sizeof(TransVert) * tvtot, "transVert"); tvtot=0; for (base=G.scene->base.first; base; base=base->next){ /* Manipulate object ipos */ tvtot=add_trans_ipo_keys(base->object->ipo, tv, tvtot); /* Manipulate object constraint ipos */ for (conchan=base->object->constraintChannels.first; conchan; conchan=conchan->next) tvtot=add_trans_ipo_keys(conchan->ipo, tv, tvtot); /* Manipulate action ipos */ if (base->object->type==OB_ARMATURE && base->object->action){ for (chan=base->object->action->chanbase.first; chan; chan=chan->next){ tvtot=add_trans_ipo_keys(chan->ipo, tv, tvtot); /* Manipulate action constraint ipos */ for (conchan=chan->constraintChannels.first; conchan; conchan=conchan->next) tvtot=add_trans_ipo_keys(conchan->ipo, tv, tvtot); } } /* Manipulate nlastrips */ for (strip=base->object->nlastrips.first; strip; strip=strip->next){ if (strip->flag & ACTSTRIP_SELECT){ tv[tvtot+0].val=&strip->start; tv[tvtot+1].val=&strip->end; tv[tvtot+0].oldval = strip->start; tv[tvtot+1].oldval = strip->end; tvtot+=2; } } } /* Do the event loop */ // cent[0] = curarea->winx + (G.snla->v2d.hor.xmax)/2; // cent[1] = curarea->winy + (G.snla->v2d.hor.ymax)/2; // areamouseco_to_ipoco(cent, &cenf[0], &cenf[1]); getmouseco_areawin (mvals); areamouseco_to_ipoco(G.v2d, mvals, &sval[0], &sval[1]); startx=sval[0]; while (loop) { /* Get the input */ /* If we're cancelling, reset transformations */ /* Else calc new transformation */ /* Perform the transformations */ while (qtest()) { short val; unsigned short event= extern_qread(&val); if (val) { switch (event) { case LEFTMOUSE: case SPACEKEY: case RETKEY: loop=0; break; case XKEY: break; case ESCKEY: case RIGHTMOUSE: cancel=1; loop=0; break; default: arrows_move_cursor(event); break; }; } } if (cancel) { for (i=0; iwinrct.xmax-curarea->winrct.xmin)/2); deltax=mvalc[0]-(NLAWIDTH/2+(curarea->winrct.xmax-curarea->winrct.xmin)/2); fac= (float)fabs(deltax/startx); apply_keyb_grid(&fac, 0.0F, 0.2F, 0.1F, U.flag & USER_AUTOSIZEGRID); if (invert){ if (i % 03 == 0){ memcpy (tv[i].loc, tv[i].oldloc, sizeof(tv[i+2].oldloc)); } if (i % 03 == 2){ memcpy (tv[i].loc, tv[i].oldloc, sizeof(tv[i-2].oldloc)); } fac*=-1; } startx= (G.scene->r.cfra); if (tv[i].loc){ tv[i].loc[0]-= startx; tv[i].loc[0]*=fac; tv[i].loc[0]+= startx; } if (tv[i].val){ tv[i].val[0]-= startx; tv[i].val[0]*=fac; tv[i].val[0]+= startx; } break; } } } if (mode=='s'){ sprintf(str, "sizeX: %.3f", fac); headerprint(str); } else if (mode=='g'){ sprintf(str, "deltaX: %.3f", fac); headerprint(str); } if (G.snla->lock){ allqueue (REDRAWVIEW3D, 0); allqueue (REDRAWNLA, 0); allqueue (REDRAWIPO, 0); force_draw_all(0); } else { addqueue (curarea->win, REDRAWALL, 0); force_draw(0); } } lastcval[0]= cval[0]; lastcval[1]= cval[1]; firsttime= 0; } if(cancel==0) BIF_undo_push("Select all NLA"); recalc_all_ipos(); // bad allqueue (REDRAWVIEW3D, 0); allqueue (REDRAWNLA, 0); allqueue (REDRAWIPO, 0); MEM_freeN (tv); } void delete_nlachannel_keys(void) { Base *base; bActionChannel *chan; bConstraintChannel *conchan; bActionStrip *strip, *nextstrip; if (!okee("Erase selected keys")) return; for (base = G.scene->base.first; base; base=base->next){ /* Delete object ipos */ delete_ipo_keys(base->object->ipo); /* Delete object constraint keys */ for(conchan=base->object->constraintChannels.first; conchan; conchan=conchan->next) delete_ipo_keys(conchan->ipo); /* Delete NLA strips */ if (base->object->type==OB_ARMATURE){ for (strip = base->object->nlastrips.first; strip; strip=nextstrip){ nextstrip=strip->next; if (strip->flag & ACTSTRIP_SELECT){ free_actionstrip(strip); BLI_remlink(&base->object->nlastrips, strip); MEM_freeN(strip); } } } /* Delete action ipos */ if (base->object->type==OB_ARMATURE && base->object->action){ for (chan=base->object->action->chanbase.first; chan; chan=chan->next){ delete_ipo_keys(chan->ipo); /* Delete action constraint keys */ for(conchan=chan->constraintChannels.first; conchan; conchan=conchan->next) delete_ipo_keys(conchan->ipo); } } } BIF_undo_push("Delete NLA keys"); recalc_all_ipos(); // bad allspace(REMAKEIPO,0); allqueue (REDRAWVIEW3D, 0); allqueue(REDRAWNLA, 0); allqueue(REDRAWIPO, 0); } void duplicate_nlachannel_keys(void) { Base *base; bActionChannel *chan; bConstraintChannel *conchan; bActionStrip *strip, *laststrip; /* Find selected items */ for (base = G.scene->base.first; base; base=base->next){ /* Duplicate object keys */ duplicate_ipo_keys(base->object->ipo); /* Duplicate object constraint keys */ for(conchan=base->object->constraintChannels.first; conchan; conchan=conchan->next) duplicate_ipo_keys(conchan->ipo); /* Duplicate nla strips */ if (base->object->type == OB_ARMATURE){ laststrip = base->object->nlastrips.last; for (strip=base->object->nlastrips.first; strip; strip=strip->next){ if (strip->flag & ACTSTRIP_SELECT){ bActionStrip *newstrip; copy_actionstrip(&newstrip, &strip); BLI_addtail(&base->object->nlastrips, newstrip); strip->flag &= ~ACTSTRIP_SELECT; newstrip->flag |= ACTSTRIP_SELECT; } if (strip==laststrip) break; } } /* Duplicate actionchannel keys */ if (base->object->type == OB_ARMATURE && base->object->action){ for (chan=base->object->action->chanbase.first; chan; chan=chan->next){ duplicate_ipo_keys(chan->ipo); /* Duplicate action constraint keys */ for(conchan=chan->constraintChannels.first; conchan; conchan=conchan->next) duplicate_ipo_keys(conchan->ipo); } } } BIF_undo_push("Duplicate NLA"); transform_nlachannel_keys ('g'); } void borderselect_nla(void) { Base *base; rcti rect; rctf rectf; int val, selectmode; short mval[2]; float ymin, ymax; bActionStrip *strip; bConstraintChannel *conchan; if ( (val = get_border (&rect, 3)) ){ if (val == LEFTMOUSE) selectmode = SELECT_ADD; else selectmode = SELECT_SUBTRACT; mval[0]= rect.xmin; mval[1]= rect.ymin+2; areamouseco_to_ipoco(G.v2d, mval, &rectf.xmin, &rectf.ymin); mval[0]= rect.xmax; mval[1]= rect.ymax-2; areamouseco_to_ipoco(G.v2d, mval, &rectf.xmax, &rectf.ymax); ymax = count_nla_levels(); ymax*= (NLACHANNELHEIGHT+NLACHANNELSKIP); for (base=G.scene->base.first; base; base=base->next){ /* Check object ipos */ if (nla_filter(base, 0)){ ymin=ymax-(NLACHANNELHEIGHT+NLACHANNELSKIP); if (base->object->ipo){ if (!((ymax < rectf.ymin) || (ymin > rectf.ymax))) borderselect_ipo_key(base->object->ipo, rectf.xmin, rectf.xmax, selectmode); } ymax=ymin; /* Check object constraint ipos */ for (conchan=base->object->constraintChannels.first; conchan; conchan=conchan->next){ ymin=ymax-(NLACHANNELHEIGHT+NLACHANNELSKIP); if (!((ymax < rectf.ymin) || (ymin > rectf.ymax))) borderselect_ipo_key(conchan->ipo, rectf.xmin, rectf.xmax, selectmode); ymax=ymin; } /* Check action ipos */ if (ACTIVE_ARMATURE(base)){ ymin=ymax-(NLACHANNELHEIGHT+NLACHANNELSKIP); if (base->object->action){ bActionChannel *chan; if (!((ymax < rectf.ymin) || (ymin > rectf.ymax))){ for (chan=base->object->action->chanbase.first; chan; chan=chan->next){ borderselect_ipo_key(chan->ipo, rectf.xmin, rectf.xmax, selectmode); /* Check action constraint ipos */ for (conchan=chan->constraintChannels.first; conchan; conchan=conchan->next) borderselect_ipo_key(conchan->ipo, rectf.xmin, rectf.xmax, selectmode); } } } ymax=ymin; } /* End of if armature */ /* Skip nlastrips */ if (base->object->type==OB_ARMATURE){ for (strip=base->object->nlastrips.first; strip; strip=strip->next){ ymin=ymax-(NLACHANNELHEIGHT+NLACHANNELSKIP); // if (!((ymax < rectf.ymin) || (ymin > rectf.ymax))){ if (!((rectf.xmaxstart) || (rectf.xmin>strip->end))){ if (val==1) strip->flag |= ACTSTRIP_SELECT; else strip->flag &= ~ACTSTRIP_SELECT; } } ymax=ymin; } } } /* End of object filter */ } BIF_undo_push("Border select NLA"); allqueue(REDRAWNLA, 0); allqueue(REDRAWACTION, 0); allqueue(REDRAWIPO, 0); } } static void mouse_nla(int selectmode) { short sel; float selx; short mval[2]; Base *base; bAction *act; bActionChannel *chan; bActionStrip *rstrip; bConstraintChannel *conchan; getmouseco_areawin (mval); /* Try object ipo selection */ base= get_nearest_nlachannel_ob_key(&selx, &sel); if (base) { if (selectmode == SELECT_REPLACE){ deselect_nlachannel_keys(0); selectmode = SELECT_ADD; } select_ipo_key(base->object->ipo, selx, selectmode); /* Try object constraint selection */ for (conchan=base->object->constraintChannels.first; conchan; conchan=conchan->next) select_ipo_key(conchan->ipo, selx, selectmode); BIF_undo_push("Select NLA"); allqueue(REDRAWIPO, 0); allqueue(REDRAWVIEW3D, 0); allqueue(REDRAWNLA, 0); return; } /* Try action ipo selection */ act= get_nearest_nlachannel_ac_key(&selx, &sel); if (act) { if (selectmode == SELECT_REPLACE){ deselect_nlachannel_keys(0); selectmode = SELECT_ADD; } for (chan=act->chanbase.first; chan; chan=chan->next) { select_ipo_key(chan->ipo, selx, selectmode); /* Try action constraint selection */ for (conchan=chan->constraintChannels.first; conchan; conchan=conchan->next) select_ipo_key(conchan->ipo, selx, selectmode); } BIF_undo_push("Select NLA"); allqueue(REDRAWIPO, 0); allqueue(REDRAWVIEW3D, 0); allqueue(REDRAWNLA, 0); return; } /* Try nla strip selection */ base= get_nearest_nlastrip(&rstrip, &sel); if (base){ if (!(G.qual & LR_SHIFTKEY)){ deselect_nlachannel_keys(0); sel = 0; } if (sel) rstrip->flag &= ~ACTSTRIP_SELECT; else rstrip->flag |= ACTSTRIP_SELECT; BIF_undo_push("Select NLA"); allqueue(REDRAWIPO, 0); allqueue(REDRAWVIEW3D, 0); allqueue(REDRAWNLA, 0); return; } } static Base *get_nearest_nlastrip (bActionStrip **rstrip, short *sel) /* This function is currently more complicated than it seems like it should be. * However, this will be needed once the nla strip timeline is more complex */ { Base *base, *firstbase=NULL; short mval[2]; short foundsel = 0; rctf rectf; float ymin, ymax; bActionStrip *strip, *firststrip=NULL, *foundstrip=NULL; bConstraintChannel *conchan=NULL; getmouseco_areawin (mval); mval[0]-=7; areamouseco_to_ipoco(G.v2d, mval, &rectf.xmin, &rectf.ymin); mval[0]+=14; areamouseco_to_ipoco(G.v2d, mval, &rectf.xmax, &rectf.ymax); ymax = count_nla_levels(); ymax*=(NLACHANNELHEIGHT + NLACHANNELSKIP); ymax+= NLACHANNELHEIGHT/2; for (base = G.scene->base.first; base; base=base->next){ if (nla_filter(base, 0)){ /* Skip object ipos */ // if (base->object->ipo) ymax-=(NLACHANNELHEIGHT+NLACHANNELSKIP); /* Skip constraint channels */ for (conchan=base->object->constraintChannels.first; conchan; conchan=conchan->next){ ymax-=(NLACHANNELHEIGHT+NLACHANNELSKIP); } if (base->object->type==OB_ARMATURE){ /* Skip action ipos */ if (base->object->action) ymax-=(NLACHANNELHEIGHT+NLACHANNELSKIP); for (strip=base->object->nlastrips.first; strip; strip=strip->next){ ymin=ymax-(NLACHANNELHEIGHT+NLACHANNELSKIP); /* Do Ytest */ if (!((ymax < rectf.ymin) || (ymin > rectf.ymax))){ /* Do XTest */ if (!((rectf.xmaxstart) || (rectf.xmin>strip->end))){ if (!firstbase){ firstbase=base; firststrip=strip; *sel = strip->flag & ACTSTRIP_SELECT; } if (strip->flag & ACTSTRIP_SELECT){ if (!foundsel){ foundsel=1; foundstrip = strip; } } else if (foundsel && strip != foundstrip){ *rstrip=strip; *sel = 0; return base; } } } ymax=ymin; } } } } *rstrip=firststrip; return firstbase; } static Base *get_nearest_nlachannel_ob_key (float *index, short *sel) { Base *base; IpoCurve *icu; Base *firstbase=NULL; bConstraintChannel *conchan; int foundsel=0; float firstvert=-1, foundx=-1; int i; short mval[2]; float ymin, ymax; rctf rectf; *index=0; getmouseco_areawin (mval); mval[0]-=7; areamouseco_to_ipoco(G.v2d, mval, &rectf.xmin, &rectf.ymin); mval[0]+=14; areamouseco_to_ipoco(G.v2d, mval, &rectf.xmax, &rectf.ymax); ymax = count_nla_levels(); ymax*= (NLACHANNELHEIGHT + NLACHANNELSKIP); ymax+= NLACHANNELHEIGHT/2; *sel=0; for (base=G.scene->base.first; base; base=base->next){ /* Handle object ipo selection */ if (nla_filter(base, 0)){ ymin=ymax-(NLACHANNELHEIGHT+NLACHANNELSKIP); if (base->object->ipo){ if (!((ymax < rectf.ymin) || (ymin > rectf.ymax))){ for (icu=base->object->ipo->curve.first; icu; icu=icu->next){ for (i=0; itotvert; i++){ if (icu->bezt[i].vec[1][0] > rectf.xmin && icu->bezt[i].vec[1][0] <= rectf.xmax ){ if (!firstbase){ firstbase=base; firstvert=icu->bezt[i].vec[1][0]; *sel = icu->bezt[i].f2 & 1; } if (icu->bezt[i].f2 & 1){ if (!foundsel){ foundsel=1; foundx = icu->bezt[i].vec[1][0]; } } else if (foundsel && icu->bezt[i].vec[1][0] != foundx){ *index=icu->bezt[i].vec[1][0]; *sel = 0; return base; } } } } } } ymax=ymin; /* Handle object constraint ipos */ for (conchan=base->object->constraintChannels.first; conchan; conchan=conchan->next){ ymin=ymax-(NLACHANNELHEIGHT+NLACHANNELSKIP); if (!((ymax < rectf.ymin) || (ymin > rectf.ymax))){ for (icu=conchan->ipo->curve.first; icu; icu=icu->next){ for (i=0; itotvert; i++){ if (icu->bezt[i].vec[1][0] > rectf.xmin && icu->bezt[i].vec[1][0] <= rectf.xmax ){ if (!firstbase){ firstbase=base; firstvert=icu->bezt[i].vec[1][0]; *sel = icu->bezt[i].f2 & 1; } if (icu->bezt[i].f2 & 1){ if (!foundsel){ foundsel=1; foundx = icu->bezt[i].vec[1][0]; } } else if (foundsel && icu->bezt[i].vec[1][0] != foundx){ *index=icu->bezt[i].vec[1][0]; *sel = 0; return base; } } } } } ymax=ymin; } /* Skip action ipos */ if (ACTIVE_ARMATURE(base)){ ymax-=(NLACHANNELHEIGHT+NLACHANNELSKIP); } /* Skip nlastrips */ if (base->object->type==OB_ARMATURE){ ymax-=(NLACHANNELHEIGHT+NLACHANNELSKIP)*BLI_countlist(&base->object->nlastrips); } } } *index=firstvert; return firstbase; } static bAction *get_nearest_nlachannel_ac_key (float *index, short *sel) { Base *base; IpoCurve *icu; bAction *firstact=NULL; int foundsel=0; float firstvert=-1, foundx=-1; int i; short mval[2]; float ymin, ymax; rctf rectf; bActionChannel *chan; bConstraintChannel *conchan; *index=0; getmouseco_areawin (mval); mval[0]-=7; areamouseco_to_ipoco(G.v2d, mval, &rectf.xmin, &rectf.ymin); mval[0]+=14; areamouseco_to_ipoco(G.v2d, mval, &rectf.xmax, &rectf.ymax); ymax = count_nla_levels(); ymax*= (NLACHANNELHEIGHT + NLACHANNELSKIP); ymax+= NLACHANNELHEIGHT/2; *sel=0; for (base=G.scene->base.first; base; base=base->next){ /* Handle object ipo selection */ if (nla_filter(base, 0)){ /* Skip object ipo */ ymin=ymax-(NLACHANNELHEIGHT+NLACHANNELSKIP); ymax=ymin; /* Handle action ipos */ if (ACTIVE_ARMATURE(base)){ ymin=ymax-(NLACHANNELHEIGHT+NLACHANNELSKIP); if (!((ymax < rectf.ymin) || (ymin > rectf.ymax))){ for (chan=base->object->action->chanbase.first; chan; chan=chan->next){ for (icu=chan->ipo->curve.first; icu; icu=icu->next){ for (i=0; itotvert; i++){ if (icu->bezt[i].vec[1][0] > rectf.xmin && icu->bezt[i].vec[1][0] <= rectf.xmax ){ if (!firstact){ firstact=base->object->action; firstvert=icu->bezt[i].vec[1][0]; *sel = icu->bezt[i].f2 & 1; } if (icu->bezt[i].f2 & 1){ if (!foundsel){ foundsel=1; foundx = icu->bezt[i].vec[1][0]; } } else if (foundsel && icu->bezt[i].vec[1][0] != foundx){ *index=icu->bezt[i].vec[1][0]; *sel = 0; return base->object->action; } } } } for (conchan=chan->constraintChannels.first; conchan; conchan=conchan->next){ ymin=ymax-(NLACHANNELHEIGHT+NLACHANNELSKIP); if (!((ymax < rectf.ymin) || (ymin > rectf.ymax))){ for (icu=conchan->ipo->curve.first; icu; icu=icu->next){ for (i=0; itotvert; i++){ if (icu->bezt[i].vec[1][0] > rectf.xmin && icu->bezt[i].vec[1][0] <= rectf.xmax ){ if (!firstact){ firstact=base->object->action; firstvert=icu->bezt[i].vec[1][0]; *sel = icu->bezt[i].f2 & 1; } if (icu->bezt[i].f2 & 1){ if (!foundsel){ foundsel=1; foundx = icu->bezt[i].vec[1][0]; } } else if (foundsel && icu->bezt[i].vec[1][0] != foundx){ *index=icu->bezt[i].vec[1][0]; *sel = 0; return base->object->action; } } } } } ymax=ymin; } } } ymax=ymin; } /* Skip nlastrips */ if (base->object->type==OB_ARMATURE){ ymax-=(NLACHANNELHEIGHT+NLACHANNELSKIP)*BLI_countlist(&base->object->nlastrips); } } } *index=firstvert; return firstact; } void clever_numbuts_nla(void){ bActionStrip *strip=NULL; int but=0; /* Determine if an nla strip has been selected */ //strip = get_active_nlastrip(); if (!strip) return; add_numbut(but++, LABEL, "Timeline Range:", 1.0, MAXFRAMEF, 0, 0); add_numbut(but++, NUM|FLO, "Strip Start:", 1.0, MAXFRAMEF, &strip->start, "First frame in the timeline"); add_numbut(but++, NUM|FLO, "Strip End:", 1.0, MAXFRAMEF, &strip->end, "Last frame in the timeline"); add_numbut(but++, LABEL, "Action Range:", 1.0, MAXFRAMEF, 0, 0); add_numbut(but++, NUM|FLO, "Action Start:", 1.0, MAXFRAMEF, &strip->actstart, "First frame of the action to map to the playrange"); add_numbut(but++, NUM|FLO, "Action End:", 1.0, MAXFRAMEF, &strip->actend, "Last frame of the action to map to the playrange"); add_numbut(but++, LABEL, "Blending:", 1.0, MAXFRAMEF, 0, 0); add_numbut(but++, NUM|FLO, "Blend In:", 0.0, MAXFRAMEF, &strip->blendin, "Number of frames of ease-in"); add_numbut(but++, NUM|FLO, "Blend Out:", 0.0, MAXFRAMEF, &strip->blendout, "Number of frames of ease-out"); add_numbut(but++, LABEL, "Options:", 1.0, MAXFRAMEF, 0, 0); add_numbut(but++, NUM|FLO, "Repeat:", 0.0001, MAXFRAMEF, &strip->repeat, "Number of times the action should repeat"); add_numbut(but++, NUM|FLO, "Stride:", 0.0001, MAXFRAMEF, &strip->stridelen, "Distance covered by one complete cycle of the action specified in the Action Range"); { /* STUPID HACK BECAUSE NUMBUTS ARE BROKEN WITH MULTIPLE TOGGLES */ short hold= (strip->flag & ACTSTRIP_HOLDLASTFRAME) ? 1 : 0; short frompath=(strip->flag & ACTSTRIP_USESTRIDE) ? 1 : 0; add_numbut(but++, TOG|SHO, "Use Path", 0, 0, &frompath, "Plays action based on position on path & stride length. Only valid for armatures that are parented to a path"); add_numbut(but++, TOG|SHO, "Hold", 0, 0, &hold, "Toggles whether or not to continue displaying the last frame past the end of the strip"); add_numbut(but++, TOG|SHO, "Add", 0, 0, &strip->mode, "Toggles additive blending mode"); do_clever_numbuts("Action", but, REDRAW); /* STUPID HACK BECAUSE NUMBUTS ARE BROKEN WITH MULTIPLE TOGGLES */ if (hold) strip->flag |= ACTSTRIP_HOLDLASTFRAME; else strip->flag &= ~ACTSTRIP_HOLDLASTFRAME; if (frompath) strip->flag |= ACTSTRIP_USESTRIDE; else strip->flag &= ~ACTSTRIP_USESTRIDE; } if (strip->endstart) strip->end=strip->start; if (strip->blendin>(strip->end-strip->start)) strip->blendin = strip->end-strip->start; if (strip->blendout>(strip->end-strip->start)) strip->blendout = strip->end-strip->start; if (strip->blendin > (strip->end-strip->start-strip->blendout)) strip->blendin = (strip->end-strip->start-strip->blendout); if (strip->blendout > (strip->end-strip->start-strip->blendin)) strip->blendout = (strip->end-strip->start-strip->blendin); update_for_newframe_muted(); allqueue (REDRAWNLA, 0); allqueue (REDRAWVIEW3D, 0); } void deselect_nlachannels(int test){ int sel = 1; Base *base; bConstraintChannel *conchan; if (test){ for (base=G.scene->base.first; base; base=base->next){ /* Check base flags for previous selection */ if (base->flag & SELECT){ sel=0; break; } /* Check constraint flags for previous selection */ for (conchan=base->object->constraintChannels.first; conchan; conchan=conchan->next){ if (conchan->flag & CONSTRAINT_CHANNEL_SELECT){ sel=0; base = G.scene->base.last; break; } } } } else sel = 0; /* Select objects */ for (base=G.scene->base.first; base; base=base->next){ if (sel){ if (nla_filter(base, 0)) base->flag |= SELECT; } else base->flag &= ~SELECT; /* Select constraint channels */ for (conchan=base->object->constraintChannels.first; conchan; conchan=conchan->next){ if (sel){ if (nla_filter(base, 0)) conchan->flag |= CONSTRAINT_CHANNEL_SELECT; } else conchan->flag &= ~CONSTRAINT_CHANNEL_SELECT; } } } void delete_nlachannels(void){ Base *base; bConstraintChannel *conchan, *nextchan; int sel=0; /* See if there is anything selected */ for (base = G.scene->base.first; base && (!sel); base=base->next){ /* Check constraints */ for (conchan=base->object->constraintChannels.first; conchan; conchan=conchan->next){ if (conchan->flag & CONSTRAINT_CHANNEL_SELECT){ sel = 1; break; } } } if (!sel) return; if (okee ("Delete selected channels")){ for (base=G.scene->base.first; base; base=base->next){ for (conchan=base->object->constraintChannels.first; conchan; conchan=nextchan){ nextchan = conchan->next; if (conchan->flag & CONSTRAINT_CHANNEL_SELECT){ /* If we're the active constraint, unlink us */ if (conchan==base->object->activecon) base->object->activecon = NULL; if (conchan->ipo) conchan->ipo->id.us--; BLI_freelinkN(&base->object->constraintChannels, conchan); } } } BIF_undo_push("Delete NLA channels"); } }