/** * $Id$ * * ***** BEGIN GPL 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. * * 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): Joshua Leung * * ***** END GPL LICENSE BLOCK ***** */ /* System includes ----------------------------------------------------- */ #include #include #include #include #ifdef HAVE_CONFIG_H #include #endif #include "MEM_guardedalloc.h" #include "BLI_blenlib.h" #include "BLI_arithb.h" /* Types --------------------------------------------------------------- */ #include "DNA_listBase.h" #include "DNA_anim_types.h" #include "DNA_action_types.h" #include "DNA_armature_types.h" #include "DNA_camera_types.h" #include "DNA_curve_types.h" #include "DNA_object_types.h" #include "DNA_screen_types.h" #include "DNA_scene_types.h" #include "DNA_space_types.h" #include "DNA_constraint_types.h" #include "DNA_key_types.h" #include "DNA_lamp_types.h" #include "DNA_material_types.h" #include "DNA_userdef_types.h" #include "DNA_gpencil_types.h" #include "DNA_windowmanager_types.h" #include "DNA_world_types.h" #include "BKE_action.h" #include "BKE_depsgraph.h" #include "BKE_fcurve.h" #include "BKE_key.h" #include "BKE_material.h" #include "BKE_object.h" #include "BKE_global.h" // XXX remove me! #include "BKE_context.h" #include "BKE_utildefines.h" /* Everything from source (BIF, BDR, BSE) ------------------------------ */ #include "BIF_gl.h" #include "BIF_glutil.h" #include "UI_interface.h" #include "UI_interface_icons.h" #include "UI_resources.h" #include "UI_view2d.h" #include "ED_anim_api.h" #include "ED_keyframing.h" #include "ED_keyframes_draw.h" #include "ED_screen.h" #include "ED_space_api.h" #if 0 // XXX old includes for reference only #include "BIF_editaction.h" #include "BIF_editkey.h" #include "BIF_editnla.h" #include "BIF_drawgpencil.h" #include "BIF_keyframing.h" #include "BIF_language.h" #include "BIF_space.h" #include "BDR_editcurve.h" #include "BDR_gpencil.h" #include "BSE_drawnla.h" #include "BSE_drawipo.h" #include "BSE_drawview.h" #include "BSE_editaction_types.h" #include "BSE_editipo.h" #include "BSE_headerbuttons.h" #include "BSE_time.h" #include "BSE_view.h" #endif // XXX old defines for reference only /* XXX */ extern void gl_round_box(int mode, float minx, float miny, float maxx, float maxy, float rad); /********************************** Slider Stuff **************************** */ #if 0 // XXX all of this slider stuff will need a rethink! /* sliders for shapekeys */ static void meshactionbuts(SpaceAction *saction, Object *ob, Key *key) { int i; char str[64]; float x, y; uiBlock *block; uiBut *but; /* lets make the shapekey sliders */ /* reset the damn myortho2 or the sliders won't draw/redraw * correctly *grumble* */ mywinset(curarea->win); myortho2(-0.375f, curarea->winx-0.375f, G.v2d->cur.ymin, G.v2d->cur.ymax); sprintf(str, "actionbuttonswin %d", curarea->win); block= uiNewBlock (&curarea->uiblocks, str, UI_EMBOSS); x = ACHANNEL_NAMEWIDTH + 1; y = 0.0f; uiBlockSetEmboss(block, UI_EMBOSSN); if (!(G.saction->flag & SACTION_SLIDERS)) { ACTWIDTH = ACHANNEL_NAMEWIDTH; but=uiDefIconButBitS(block, TOG, SACTION_SLIDERS, B_REDR, ICON_DISCLOSURE_TRI_RIGHT, ACHANNEL_NAMEWIDTH - XIC - 5, (short)y + CHANNELHEIGHT, XIC,YIC-2, &(G.saction->flag), 0, 0, 0, 0, "Show action window sliders"); /* no hilite, the winmatrix is not correct later on... */ uiButSetFlag(but, UI_NO_HILITE); } else { but= uiDefIconButBitS(block, TOG, SACTION_SLIDERS, B_REDR, ICON_DISCLOSURE_TRI_DOWN, ACHANNEL_NAMEWIDTH - XIC - 5, (short)y + CHANNELHEIGHT, XIC,YIC-2, &(G.saction->flag), 0, 0, 0, 0, "Hide action window sliders"); /* no hilite, the winmatrix is not correct later on... */ uiButSetFlag(but, UI_NO_HILITE); ACTWIDTH = ACHANNEL_NAMEWIDTH + SLIDERWIDTH; /* sliders are open so draw them */ BIF_ThemeColor(TH_FACE); glRects(ACHANNEL_NAMEWIDTH, 0, ACHANNEL_NAMEWIDTH+SLIDERWIDTH, curarea->winy); uiBlockSetEmboss(block, UI_EMBOSS); for (i=1; i < key->totkey; i++) { make_rvk_slider(block, ob, i, (int)x, (int)y, SLIDERWIDTH-2, CHANNELHEIGHT-1, "Slider to control Shape Keys"); y-=CHANNELHEIGHT+CHANNELSKIP; /* see sliderval array in editkey.c */ if (i >= 255) break; } } uiDrawBlock(C, block); } static void icu_slider_func(void *voidicu, void *voidignore) { /* the callback for the icu sliders ... copies the * value from the icu->curval into a bezier at the * right frame on the right ipo curve (creating both the * ipo curve and the bezier if needed). */ IpoCurve *icu= voidicu; BezTriple *bezt=NULL; float cfra, icuval; cfra = frame_to_float(CFRA); if (G.saction->pin==0 && OBACT) cfra= get_action_frame(OBACT, cfra); /* if the ipocurve exists, try to get a bezier * for this frame */ bezt = get_bezt_icu_time(icu, &cfra, &icuval); /* create the bezier triple if one doesn't exist, * otherwise modify it's value */ if (bezt == NULL) { insert_vert_icu(icu, cfra, icu->curval, 0); } else { bezt->vec[1][1] = icu->curval; } /* make sure the Ipo's are properly processed and * redraw as necessary */ sort_time_ipocurve(icu); testhandles_ipocurve(icu); /* nla-update (in case this affects anything) */ synchronize_action_strips(); /* do redraw pushes, and also the depsgraph flushes */ if (OBACT->pose || ob_get_key(OBACT)) DAG_object_flush_update(G.scene, OBACT, OB_RECALC); else DAG_object_flush_update(G.scene, OBACT, OB_RECALC_OB); allqueue(REDRAWVIEW3D, 0); allqueue(REDRAWACTION, 0); allqueue(REDRAWNLA, 0); allqueue(REDRAWIPO, 0); allspace(REMAKEIPO, 0); allqueue(REDRAWBUTSALL, 0); } static void make_icu_slider(uiBlock *block, IpoCurve *icu, int x, int y, int w, int h, char *tip) { /* create a slider for the ipo-curve*/ uiBut *but; if(icu == NULL) return; if (IS_EQ(icu->slide_max, icu->slide_min)) { if (IS_EQ(icu->ymax, icu->ymin)) { if (ELEM(icu->blocktype, ID_CO, ID_KE)) { /* hack for constraints and shapekeys (and maybe a few others) */ icu->slide_min= 0.0; icu->slide_max= 1.0; } else { icu->slide_min= -100; icu->slide_max= 100; } } else { icu->slide_min= icu->ymin; icu->slide_max= icu->ymax; } } if (icu->slide_min >= icu->slide_max) { SWAP(float, icu->slide_min, icu->slide_max); } but=uiDefButF(block, NUMSLI, REDRAWVIEW3D, "", x, y , w, h, &(icu->curval), icu->slide_min, icu->slide_max, 10, 2, tip); uiButSetFunc(but, icu_slider_func, icu, NULL); // no hilite, the winmatrix is not correct later on... uiButSetFlag(but, UI_NO_HILITE); } /* sliders for ipo-curves of active action-channel */ static void action_icu_buts(SpaceAction *saction) { ListBase act_data = {NULL, NULL}; bActListElem *ale; int filter; void *data; short datatype; char str[64]; float x, y; uiBlock *block; /* lets make the action sliders */ /* reset the damn myortho2 or the sliders won't draw/redraw * correctly *grumble* */ mywinset(curarea->win); myortho2(-0.375f, curarea->winx-0.375f, G.v2d->cur.ymin, G.v2d->cur.ymax); sprintf(str, "actionbuttonswin %d", curarea->win); block= uiNewBlock (&curarea->uiblocks, str, UI_EMBOSS); x = (float)ACHANNEL_NAMEWIDTH + 1; y = 0.0f; uiBlockSetEmboss(block, UI_EMBOSSN); if (G.saction->flag & SACTION_SLIDERS) { /* sliders are open so draw them */ /* get editor data */ data= get_action_context(&datatype); if (data == NULL) return; /* build list of channels to draw */ filter= (ACTFILTER_FORDRAWING|ACTFILTER_VISIBLE|ACTFILTER_CHANNELS); actdata_filter(&act_data, filter, data, datatype); /* draw backdrop first */ BIF_ThemeColor(TH_FACE); // change this color... it's ugly glRects(ACHANNEL_NAMEWIDTH, (short)G.v2d->cur.ymin, ACHANNEL_NAMEWIDTH+SLIDERWIDTH, (short)G.v2d->cur.ymax); uiBlockSetEmboss(block, UI_EMBOSS); for (ale= act_data.first; ale; ale= ale->next) { const float yminc= y-CHANNELHEIGHT/2; const float ymaxc= y+CHANNELHEIGHT/2; /* check if visible */ if ( IN_RANGE(yminc, G.v2d->cur.ymin, G.v2d->cur.ymax) || IN_RANGE(ymaxc, G.v2d->cur.ymin, G.v2d->cur.ymax) ) { /* determine what needs to be drawn */ switch (ale->type) { case ACTTYPE_CONCHAN: /* constraint channel */ { bActionChannel *achan = (bActionChannel *)ale->owner; IpoCurve *icu = (IpoCurve *)ale->key_data; /* only show if owner is selected */ if ((ale->ownertype == ACTTYPE_OBJECT) || SEL_ACHAN(achan)) { make_icu_slider(block, icu, (int)x, (int)y, SLIDERWIDTH-2, CHANNELHEIGHT-2, "Slider to control current value of Constraint Influence"); } } break; case ACTTYPE_ICU: /* ipo-curve channel */ { bActionChannel *achan = (bActionChannel *)ale->owner; IpoCurve *icu = (IpoCurve *)ale->key_data; /* only show if owner is selected */ if ((ale->ownertype == ACTTYPE_OBJECT) || SEL_ACHAN(achan)) { make_icu_slider(block, icu, (int)x, (int)y, SLIDERWIDTH-2, CHANNELHEIGHT-2, "Slider to control current value of IPO-Curve"); } } break; case ACTTYPE_SHAPEKEY: /* shapekey channel */ { Object *ob= (Object *)ale->id; IpoCurve *icu= (IpoCurve *)ale->key_data; // TODO: only show if object is active if (icu) { make_icu_slider(block, icu, (int)x, (int)y, SLIDERWIDTH-2, CHANNELHEIGHT-2, "Slider to control ShapeKey"); } else if (ob && ale->index) { make_rvk_slider(block, ob, ale->index, (int)x, (int)y, SLIDERWIDTH-2, CHANNELHEIGHT-1, "Slider to control Shape Keys"); } } break; } } /* adjust y-position for next one */ y-=CHANNELHEIGHT+CHANNELSKIP; } /* free tempolary channels */ BLI_freelistN(&act_data); } uiDrawBlock(C, block); } #endif // XXX all of this slider stuff will need a rethink /* ************************************************************************* */ /* Channel List */ /* left hand part */ void draw_channel_names(bAnimContext *ac, SpaceAction *saction, ARegion *ar) { ListBase anim_data = {NULL, NULL}; bAnimListElem *ale; int filter; View2D *v2d= &ar->v2d; float x= 0.0f, y= 0.0f; int items, height; /* build list of channels to draw */ filter= (ANIMFILTER_VISIBLE|ANIMFILTER_CHANNELS); items= ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); /* Update max-extent of channels here (taking into account scrollers): * - this is done to allow the channel list to be scrollable, but must be done here * to avoid regenerating the list again and/or also because channels list is drawn first * - offset of ACHANNEL_HEIGHT*2 is added to the height of the channels, as first is for * start of list offset, and the second is as a correction for the scrollers. */ height= ((items*ACHANNEL_STEP) + (ACHANNEL_HEIGHT*2)); if (height > (v2d->mask.ymax - v2d->mask.ymin)) { /* don't use totrect set, as the width stays the same * (NOTE: this is ok here, the configuration is pretty straightforward) */ v2d->tot.ymin= (float)(-height); } /* need to do a view-sync here, so that the keys area doesn't jump around */ UI_view2d_sync(NULL, ac->sa, v2d, V2D_VIEWSYNC_AREA_VERTICAL); /* loop through channels, and set up drawing depending on their type */ y= (float)ACHANNEL_FIRST; for (ale= anim_data.first; ale; ale= ale->next) { const float yminc= (float)(y - ACHANNEL_HEIGHT_HALF); const float ymaxc= (float)(y + ACHANNEL_HEIGHT_HALF); /* check if visible */ if ( IN_RANGE(yminc, v2d->cur.ymin, v2d->cur.ymax) || IN_RANGE(ymaxc, v2d->cur.ymin, v2d->cur.ymax) ) { bActionGroup *grp = NULL; short indent= 0, offset= 0, sel= 0, group= 0; int expand= -1, protect = -1, special= -1, mute = -1; char name[128]; /* determine what needs to be drawn */ switch (ale->type) { case ANIMTYPE_SCENE: /* scene */ { Scene *sce= (Scene *)ale->data; group= 4; indent= 0; special= ICON_SCENE_DATA; /* only show expand if there are any channels */ if (EXPANDED_SCEC(sce)) expand= ICON_TRIA_DOWN; else expand= ICON_TRIA_RIGHT; sel = SEL_SCEC(sce); strcpy(name, sce->id.name+2); } break; case ANIMTYPE_OBJECT: /* object */ { Base *base= (Base *)ale->data; Object *ob= base->object; group= 4; indent= 0; /* icon depends on object-type */ if (ob->type == OB_ARMATURE) special= ICON_ARMATURE_DATA; else special= ICON_OBJECT_DATA; /* only show expand if there are any channels */ if (EXPANDED_OBJC(ob)) expand= ICON_TRIA_DOWN; else expand= ICON_TRIA_RIGHT; sel = SEL_OBJC(base); strcpy(name, ob->id.name+2); } break; case ANIMTYPE_FILLACTD: /* action widget */ { bAction *act= (bAction *)ale->data; group = 4; indent= 1; special= ICON_ACTION; if (EXPANDED_ACTC(act)) expand= ICON_TRIA_DOWN; else expand= ICON_TRIA_RIGHT; sel = SEL_ACTC(act); strcpy(name, act->id.name+2); } break; case ANIMTYPE_FILLMATD: /* object materials (dopesheet) expand widget */ { Object *ob = (Object *)ale->data; group = 4; indent = 1; special = ICON_MATERIAL_DATA; if (FILTER_MAT_OBJC(ob)) expand = ICON_TRIA_DOWN; else expand = ICON_TRIA_RIGHT; strcpy(name, "Materials"); } break; case ANIMTYPE_DSMAT: /* single material (dopesheet) expand widget */ { Material *ma = (Material *)ale->data; group = 0; indent = 0; special = ICON_MATERIAL_DATA; offset = 21; if (FILTER_MAT_OBJD(ma)) expand = ICON_TRIA_DOWN; else expand = ICON_TRIA_RIGHT; strcpy(name, ma->id.name+2); } break; case ANIMTYPE_DSLAM: /* lamp (dopesheet) expand widget */ { Lamp *la = (Lamp *)ale->data; group = 4; indent = 1; special = ICON_LAMP_DATA; if (FILTER_LAM_OBJD(la)) expand = ICON_TRIA_DOWN; else expand = ICON_TRIA_RIGHT; strcpy(name, la->id.name+2); } break; case ANIMTYPE_DSCAM: /* camera (dopesheet) expand widget */ { Camera *ca = (Camera *)ale->data; group = 4; indent = 1; special = ICON_CAMERA_DATA; if (FILTER_CAM_OBJD(ca)) expand = ICON_TRIA_DOWN; else expand = ICON_TRIA_RIGHT; strcpy(name, ca->id.name+2); } break; case ANIMTYPE_DSCUR: /* curve (dopesheet) expand widget */ { Curve *cu = (Curve *)ale->data; group = 4; indent = 1; special = ICON_CURVE_DATA; if (FILTER_CUR_OBJD(cu)) expand = ICON_TRIA_DOWN; else expand = ICON_TRIA_RIGHT; strcpy(name, cu->id.name+2); } break; case ANIMTYPE_DSSKEY: /* shapekeys (dopesheet) expand widget */ { Key *key= (Key *)ale->data; group = 4; indent = 1; special = ICON_SHAPEKEY_DATA; // XXX if (FILTER_SKE_OBJD(key)) expand = ICON_TRIA_DOWN; else expand = ICON_TRIA_RIGHT; //sel = SEL_OBJC(base); strcpy(name, "Shape Keys"); } break; case ANIMTYPE_DSWOR: /* world (dopesheet) expand widget */ { World *wo= (World *)ale->data; group = 4; indent = 1; special = ICON_WORLD_DATA; if (FILTER_WOR_SCED(wo)) expand = ICON_TRIA_DOWN; else expand = ICON_TRIA_RIGHT; strcpy(name, wo->id.name+2); } break; case ANIMTYPE_GROUP: /* action group */ { bActionGroup *agrp= (bActionGroup *)ale->data; group= 2; indent= 0; special= -1; if (ale->id) { /* special exception for materials */ if (GS(ale->id->name) == ID_MA) offset= 25; else offset= 14; } else offset= 0; /* only show expand if there are any channels */ if (agrp->channels.first) { if (EXPANDED_AGRP(agrp)) expand = ICON_TRIA_DOWN; else expand = ICON_TRIA_RIGHT; } if (agrp->flag & AGRP_MUTED) mute = ICON_MUTE_IPO_ON; else mute = ICON_MUTE_IPO_OFF; if (EDITABLE_AGRP(agrp)) protect = ICON_UNLOCKED; else protect = ICON_LOCKED; sel = SEL_AGRP(agrp); strcpy(name, agrp->name); } break; case ANIMTYPE_FCURVE: /* F-Curve channel */ { FCurve *fcu = (FCurve *)ale->data; indent = 0; group= (fcu->grp) ? 1 : 0; grp= fcu->grp; if (ale->id) { /* special exception for materials */ if (GS(ale->id->name) == ID_MA) { offset= 21; indent= 1; } else offset= 14; } else offset= 0; if (fcu->flag & FCURVE_MUTED) mute = ICON_MUTE_IPO_ON; else mute = ICON_MUTE_IPO_OFF; if (fcu->bezt) { if (EDITABLE_FCU(fcu)) protect = ICON_UNLOCKED; else protect = ICON_LOCKED; } else protect = ICON_ZOOMOUT; // XXX editability is irrelevant here, but this icon is temp... sel = SEL_FCU(fcu); getname_anim_fcurve(name, ale->id, fcu); } break; case ANIMTYPE_SHAPEKEY: /* shapekey channel */ { KeyBlock *kb = (KeyBlock *)ale->data; indent = 0; special = -1; offset= (ale->id) ? 21 : 0; if (kb->name[0] == '\0') sprintf(name, "Key %d", ale->index); else strcpy(name, kb->name); } break; case ANIMTYPE_GPDATABLOCK: /* gpencil datablock */ { bGPdata *gpd = (bGPdata *)ale->data; ScrArea *sa = (ScrArea *)ale->owner; indent = 0; group= 3; /* only show expand if there are any channels */ if (gpd->layers.first) { if (gpd->flag & GP_DATA_EXPAND) expand = ICON_TRIA_DOWN; else expand = ICON_TRIA_RIGHT; } switch (sa->spacetype) { case SPACE_VIEW3D: { /* this shouldn't cause any overflow... */ //sprintf(name, "3DView:%s", view3d_get_name(sa->spacedata.first)); // XXX missing func.. strcpy(name, "3dView"); special= ICON_VIEW3D; } break; case SPACE_NODE: { SpaceNode *snode= sa->spacedata.first; char treetype[12]; if (snode->treetype == 1) strcpy(treetype, "Composite"); else strcpy(treetype, "Material"); sprintf(name, "Nodes:%s", treetype); special= ICON_NODE; } break; case SPACE_SEQ: { SpaceSeq *sseq= sa->spacedata.first; char imgpreview[10]; switch (sseq->mainb) { case 1: sprintf(imgpreview, "Image..."); break; case 2: sprintf(imgpreview, "Luma..."); break; case 3: sprintf(imgpreview, "Chroma..."); break; case 4: sprintf(imgpreview, "Histogram"); break; default: sprintf(imgpreview, "Sequence"); break; } sprintf(name, "Sequencer:%s", imgpreview); special= ICON_SEQUENCE; } break; case SPACE_IMAGE: { SpaceImage *sima= sa->spacedata.first; if (sima->image) sprintf(name, "Image:%s", sima->image->id.name+2); else strcpy(name, "Image:"); special= ICON_IMAGE_COL; } break; default: { sprintf(name, ""); special= -1; } break; } } break; case ANIMTYPE_GPLAYER: /* gpencil layer */ { bGPDlayer *gpl = (bGPDlayer *)ale->data; indent = 0; special = -1; expand = -1; group = 1; if (EDITABLE_GPL(gpl)) protect = ICON_UNLOCKED; else protect = ICON_LOCKED; if (gpl->flag & GP_LAYER_HIDE) mute = ICON_MUTE_IPO_ON; else mute = ICON_MUTE_IPO_OFF; sel = SEL_GPL(gpl); BLI_snprintf(name, 32, gpl->info); } break; } /* now, start drawing based on this information */ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glEnable(GL_BLEND); /* draw backing strip behind channel name */ if (group == 4) { /* only used in dopesheet... */ if (ELEM(ale->type, ANIMTYPE_SCENE, ANIMTYPE_OBJECT)) { /* object channel - darker */ UI_ThemeColor(TH_DOPESHEET_CHANNELOB); uiSetRoundBox((expand == ICON_TRIA_DOWN)? (1):(1|8)); gl_round_box(GL_POLYGON, x+offset, yminc, (float)ACHANNEL_NAMEWIDTH, ymaxc, 8); } else { /* sub-object folders - lighter */ UI_ThemeColor(TH_DOPESHEET_CHANNELSUBOB); offset += 7 * indent; glBegin(GL_QUADS); glVertex2f(x+offset, yminc); glVertex2f(x+offset, ymaxc); glVertex2f((float)ACHANNEL_NAMEWIDTH, ymaxc); glVertex2f((float)ACHANNEL_NAMEWIDTH, yminc); glEnd(); /* clear group value, otherwise we cause errors... */ group = 0; } } else if (group == 3) { /* only for gp-data channels */ UI_ThemeColorShade(TH_GROUP, 20); uiSetRoundBox((expand == ICON_TRIA_DOWN)? (1):(1|8)); gl_round_box(GL_POLYGON, x+offset, yminc, (float)ACHANNEL_NAMEWIDTH, ymaxc, 8); } else if (group == 2) { /* only for action group channels */ if (ale->flag & AGRP_ACTIVE) UI_ThemeColorShade(TH_GROUP_ACTIVE, 10); else UI_ThemeColorShade(TH_GROUP, 20); uiSetRoundBox((expand == ICON_TRIA_DOWN)? (1):(1|8)); gl_round_box(GL_POLYGON, x+offset, yminc, (float)ACHANNEL_NAMEWIDTH, ymaxc, 8); } else { /* for normal channels * - use 3 shades of color group/standard color for 3 indention level * - only use group colors if allowed to, and if actually feasible */ if ( !(saction->flag & SACTION_NODRAWGCOLORS) && (grp) && (grp->customCol) ) { char cp[3]; if (indent == 2) { VECCOPY(cp, grp->cs.solid); } else if (indent == 1) { VECCOPY(cp, grp->cs.select); } else { VECCOPY(cp, grp->cs.active); } glColor3ub(cp[0], cp[1], cp[2]); } else UI_ThemeColorShade(TH_HEADER, ((indent==0)?20: (indent==1)?-20: -40)); indent += group; offset += 7 * indent; glBegin(GL_QUADS); glVertex2f(x+offset, yminc); glVertex2f(x+offset, ymaxc); glVertex2f((float)ACHANNEL_NAMEWIDTH, ymaxc); glVertex2f((float)ACHANNEL_NAMEWIDTH, yminc); glEnd(); } /* draw expand/collapse triangle */ if (expand > 0) { UI_icon_draw(x+offset, yminc, expand); offset += 17; } /* draw special icon indicating certain data-types */ if (special > -1) { if (ELEM(group, 3, 4)) { /* for gpdatablock channels */ UI_icon_draw(x+offset, yminc, special); offset += 17; } else { /* for normal channels */ UI_icon_draw(x+offset, yminc, special); offset += 17; } } glDisable(GL_BLEND); /* draw name */ if (sel) UI_ThemeColor(TH_TEXT_HI); else UI_ThemeColor(TH_TEXT); offset += 3; UI_DrawString(x+offset, y-4, name); /* reset offset - for RHS of panel */ offset = 0; /* set blending again, as text drawing may clear it */ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glEnable(GL_BLEND); /* draw protect 'lock' */ if (protect > -1) { offset = 16; UI_icon_draw((float)ACHANNEL_NAMEWIDTH-offset, yminc, protect); } /* draw mute 'eye' */ if (mute > -1) { offset += 16; UI_icon_draw((float)(ACHANNEL_NAMEWIDTH-offset), yminc, mute); } glDisable(GL_BLEND); } /* adjust y-position for next one */ y -= ACHANNEL_STEP; } /* free tempolary channels */ BLI_freelistN(&anim_data); } /* ************************************************************************* */ /* Keyframes */ /* extra padding for lengths (to go under scrollers) */ #define EXTRA_SCROLL_PAD 100.0f /* draw keyframes in each channel */ void draw_channel_strips(bAnimContext *ac, SpaceAction *saction, ARegion *ar) { ListBase anim_data = {NULL, NULL}; bAnimListElem *ale; int filter; View2D *v2d= &ar->v2d; bDopeSheet *ads= &saction->ads; AnimData *adt= NULL; float act_start, act_end, y; int height, items; char col1[3], col2[3]; char col1a[3], col2a[3]; char col1b[3], col2b[3]; /* get theme colors */ UI_GetThemeColor3ubv(TH_BACK, col2); UI_GetThemeColor3ubv(TH_HILITE, col1); UI_GetThemeColor3ubv(TH_GROUP, col2a); UI_GetThemeColor3ubv(TH_GROUP_ACTIVE, col1a); UI_GetThemeColor3ubv(TH_DOPESHEET_CHANNELOB, col1b); UI_GetThemeColor3ubv(TH_DOPESHEET_CHANNELSUBOB, col2b); /* set view-mapping rect (only used for x-axis), for NLA-scaling mapping with less calculation */ /* if in NLA there's a strip active, map the view */ if (ac->datatype == ANIMCONT_ACTION) { adt= ANIM_nla_mapping_get(ac, NULL); /* start and end of action itself */ // TODO: this has not had scaling applied calc_action_range(ac->data, &act_start, &act_end, 0); } /* build list of channels to draw */ filter= (ANIMFILTER_VISIBLE|ANIMFILTER_CHANNELS); items= ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); /* Update max-extent of channels here (taking into account scrollers): * - this is done to allow the channel list to be scrollable, but must be done here * to avoid regenerating the list again and/or also because channels list is drawn first * - offset of ACHANNEL_HEIGHT*2 is added to the height of the channels, as first is for * start of list offset, and the second is as a correction for the scrollers. */ height= ((items*ACHANNEL_STEP) + (ACHANNEL_HEIGHT*2)); /* don't use totrect set, as the width stays the same * (NOTE: this is ok here, the configuration is pretty straightforward) */ v2d->tot.ymin= (float)(-height); /* first backdrop strips */ y= (float)(-ACHANNEL_HEIGHT); glEnable(GL_BLEND); for (ale= anim_data.first; ale; ale= ale->next) { const float yminc= (float)(y - ACHANNEL_HEIGHT_HALF); const float ymaxc= (float)(y + ACHANNEL_HEIGHT_HALF); /* check if visible */ if ( IN_RANGE(yminc, v2d->cur.ymin, v2d->cur.ymax) || IN_RANGE(ymaxc, v2d->cur.ymin, v2d->cur.ymax) ) { int sel=0; /* determine if any need to draw channel */ if (ale->datatype != ALE_NONE) { /* determine if channel is selected */ switch (ale->type) { case ANIMTYPE_SCENE: { Scene *sce= (Scene *)ale->data; sel = SEL_SCEC(sce); } break; case ANIMTYPE_OBJECT: { Base *base= (Base *)ale->data; sel = SEL_OBJC(base); } break; case ANIMTYPE_GROUP: { bActionGroup *agrp = (bActionGroup *)ale->data; sel = SEL_AGRP(agrp); } break; case ANIMTYPE_FCURVE: { FCurve *fcu = (FCurve *)ale->data; sel = SEL_FCU(fcu); } break; case ANIMTYPE_GPLAYER: { bGPDlayer *gpl = (bGPDlayer *)ale->data; sel = SEL_GPL(gpl); } break; } if (ELEM(ac->datatype, ANIMCONT_ACTION, ANIMCONT_DOPESHEET)) { switch (ale->type) { case ANIMTYPE_SCENE: case ANIMTYPE_OBJECT: { if (sel) glColor4ub(col1b[0], col1b[1], col1b[2], 0x45); else glColor4ub(col1b[0], col1b[1], col1b[2], 0x22); } break; case ANIMTYPE_FILLACTD: case ANIMTYPE_FILLMATD: case ANIMTYPE_DSSKEY: case ANIMTYPE_DSWOR: { if (sel) glColor4ub(col2b[0], col2b[1], col2b[2], 0x45); else glColor4ub(col2b[0], col2b[1], col2b[2], 0x22); } break; case ANIMTYPE_GROUP: { if (sel) glColor4ub(col1a[0], col1a[1], col1a[2], 0x22); else glColor4ub(col2a[0], col2a[1], col2a[2], 0x22); } break; default: { if (sel) glColor4ub(col1[0], col1[1], col1[2], 0x22); else glColor4ub(col2[0], col2[1], col2[2], 0x22); } break; } /* draw region twice: firstly backdrop, then the current range */ glRectf(v2d->cur.xmin, (float)y-ACHANNEL_HEIGHT_HALF, v2d->cur.xmax+EXTRA_SCROLL_PAD, (float)y+ACHANNEL_HEIGHT_HALF); if (ac->datatype == ANIMCONT_ACTION) glRectf(act_start, (float)y-ACHANNEL_HEIGHT_HALF, act_end, (float)y+ACHANNEL_HEIGHT_HALF); } else if (ac->datatype == ANIMCONT_SHAPEKEY) { /* all frames that have a frame number less than one * get a desaturated orange background */ glColor4ub(col2[0], col2[1], col2[2], 0x22); glRectf(0.0f, (float)y-ACHANNEL_HEIGHT_HALF, 1.0f, (float)y+ACHANNEL_HEIGHT_HALF); /* frames one and higher get a saturated orange background */ glColor4ub(col2[0], col2[1], col2[2], 0x44); glRectf(1.0f, (float)y-ACHANNEL_HEIGHT_HALF, v2d->cur.xmax+EXTRA_SCROLL_PAD, (float)y+ACHANNEL_HEIGHT_HALF); } else if (ac->datatype == ANIMCONT_GPENCIL) { /* frames less than one get less saturated background */ if (sel) glColor4ub(col1[0], col1[1], col1[2], 0x22); else glColor4ub(col2[0], col2[1], col2[2], 0x22); glRectf(0.0f, (float)y-ACHANNEL_HEIGHT_HALF, v2d->cur.xmin, (float)y+ACHANNEL_HEIGHT_HALF); /* frames one and higher get a saturated background */ if (sel) glColor4ub(col1[0], col1[1], col1[2], 0x44); else glColor4ub(col2[0], col2[1], col2[2], 0x44); glRectf(v2d->cur.xmin, (float)y-ACHANNEL_HEIGHT_HALF, v2d->cur.xmax+EXTRA_SCROLL_PAD, (float)y+ACHANNEL_HEIGHT_HALF); } } } /* Increment the step */ y -= ACHANNEL_STEP; } glDisable(GL_BLEND); /* Draw keyframes * 1) Only channels that are visible in the Action Editor get drawn/evaluated. * This is to try to optimise this for heavier data sets * 2) Keyframes which are out of view horizontally are disregarded */ y= (float)(-ACHANNEL_HEIGHT); for (ale= anim_data.first; ale; ale= ale->next) { const float yminc= (float)(y - ACHANNEL_HEIGHT_HALF); const float ymaxc= (float)(y + ACHANNEL_HEIGHT_HALF); /* check if visible */ if ( IN_RANGE(yminc, v2d->cur.ymin, v2d->cur.ymax) || IN_RANGE(ymaxc, v2d->cur.ymin, v2d->cur.ymax) ) { /* check if anything to show for this channel */ if (ale->datatype != ALE_NONE) { adt= ANIM_nla_mapping_get(ac, ale); /* draw 'keyframes' for each specific datatype */ switch (ale->datatype) { case ALE_SCE: draw_scene_channel(v2d, ads, ale->key_data, y); break; case ALE_OB: draw_object_channel(v2d, ads, ale->key_data, y); break; case ALE_ACT: draw_action_channel(v2d, adt, ale->key_data, y); break; case ALE_GROUP: draw_agroup_channel(v2d, adt, ale->data, y); break; case ALE_FCURVE: draw_fcurve_channel(v2d, adt, ale->key_data, y); break; case ALE_GPFRAME: draw_gpl_channel(v2d, ads, ale->data, y); break; } } } y-= ACHANNEL_STEP; } /* free tempolary channels used for drawing */ BLI_freelistN(&anim_data); /* black line marking 'current frame' for Time-Slide transform mode */ if (saction->flag & SACTION_MOVING) { glColor3f(0.0f, 0.0f, 0.0f); glBegin(GL_LINES); glVertex2f(saction->timeslide, v2d->cur.ymin-EXTRA_SCROLL_PAD); glVertex2f(saction->timeslide, v2d->cur.ymax); glEnd(); } }