/*==LICENSE==* CyanWorlds.com Engine - MMOG client, server and tools Copyright (C) 2011 Cyan Worlds, Inc. 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 3 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, see . You can contact Cyan Worlds, Inc. by email legal@cyan.com or by snail mail at: Cyan Worlds, Inc. 14617 N Newport Hwy Mead, WA 99021 *==LICENSE==*/ ////////////////////////////////////////////////////////////////////////////// // // // pfGUIControlMod Definition // // // ////////////////////////////////////////////////////////////////////////////// #include "hsTypes.h" #include "pfGUIControlMod.h" #include "pfGameGUIMgr.h" #include "pfGUIDialogMod.h" #include "pfGUIControlHandlers.h" #include "pfGUIDialogHandlers.h" #include "pfGUIListElement.h" // Includes dropTargetProc #include "../pnMessage/plRefMsg.h" #include "../pnMessage/plEnableMsg.h" #include "../pfMessage/pfGameGUIMsg.h" #include "../pnSceneObject/plDrawInterface.h" #include "../pnSceneObject/plCoordinateInterface.h" #include "../pnSceneObject/plAudioInterface.h" #include "../plGImage/plDynamicTextMap.h" #include "../plSurface/plLayer.h" #include "../plMessage/plRenderMsg.h" #include "../pnMessage/plSoundMsg.h" #include "plPipeline.h" #include "../plDrawable/plAccessGeometry.h" #include "../plDrawable/plAccessSpan.h" #include "../plDrawable/plAccessVtxSpan.h" #include "pfGUIPopUpMenu.h" // For skin, can we move that please? Thank you #include "plgDispatch.h" #include "hsResMgr.h" //// pfGUIColorScheme Functions ////////////////////////////////////////////// void pfGUIColorScheme::IReset( void ) { fForeColor.Set( 1, 1, 1, 1 ); fBackColor.Set( 0, 0, 0, 1 ); fSelForeColor.Set( 1, 1, 1, 1 ); fSelBackColor.Set( 0, 0, 1, 1 ); fTransparent = false; fFontFace = hsStrcpy( "Times New Roman" ); fFontSize = 10; fFontFlags = 0; } pfGUIColorScheme::pfGUIColorScheme() { IReset(); } pfGUIColorScheme::~pfGUIColorScheme() { delete [] fFontFace; } pfGUIColorScheme::pfGUIColorScheme( hsColorRGBA &foreColor, hsColorRGBA &backColor ) { IReset(); fForeColor = foreColor; fBackColor = backColor; } pfGUIColorScheme::pfGUIColorScheme( const char *face, UInt8 size, UInt8 fontFlags ) { IReset(); fFontFace = hsStrcpy( face ); fFontSize = size; fFontFlags = fontFlags; } void pfGUIColorScheme::SetFontFace( const char *face ) { delete [] fFontFace; fFontFace = hsStrcpy( face ); } void pfGUIColorScheme::Read( hsStream *s ) { fForeColor.Read( s ); fBackColor.Read( s ); fSelForeColor.Read( s ); fSelBackColor.Read( s ); s->ReadSwap( &fTransparent ); delete [] fFontFace; fFontFace = s->ReadSafeString(); s->ReadSwap( &fFontSize ); s->ReadSwap( &fFontFlags ); } void pfGUIColorScheme::Write( hsStream *s ) { fForeColor.Write( s ); fBackColor.Write( s ); fSelForeColor.Write( s ); fSelBackColor.Write( s ); s->WriteSwap( fTransparent ); s->WriteSafeString( fFontFace ); s->WriteSwap( fFontSize ); s->WriteSwap( fFontFlags ); } //// Constructor/Destructor ////////////////////////////////////////////////// pfGUIControlMod::pfGUIControlMod() { fEnabled = true; fDialog = nil; fBoundsValid = false; fCenterValid = false; fFocused = false; fInteresting = false; fVisible = true; fHandler = nil; fTagID = 0; fDropTargetHdlr = nil; fDynTextMap = nil; fProxy = nil; fColorScheme = nil; fSkin = nil; fNotifyOnInteresting = false; } pfGUIControlMod::~pfGUIControlMod() { ISetHandler( nil ); SetDropTargetHdlr( nil ); SetColorScheme( nil ); } //// IEval /////////////////////////////////////////////////////////////////// hsBool pfGUIControlMod::IEval( double secs, hsScalar del, UInt32 dirty ) { // UpdateBounds(); return false; } //// GetBounds /////////////////////////////////////////////////////////////// const hsBounds3 &pfGUIControlMod::GetBounds( void ) { UpdateBounds(); return fBounds; } //// SetTransform //////////////////////////////////////////////////////////// // Override from plModifier so we can update our bounds void pfGUIControlMod::SetTransform( const hsMatrix44 &l2w, const hsMatrix44 &w2l ) { fBoundsValid = false; } //// GetVectorAngle ////////////////////////////////////////////////////////// static hsScalar GetVectorAngle( const hsPoint3 &basePt, const hsPoint3 &pointA, const hsPoint3 &pointB ) { hsVector3 vectorA( &pointA, &basePt ), vectorB( &pointB, &basePt ); hsScalar dot = vectorA * vectorB; hsVector3 cross = vectorA % vectorB; hsScalar crossLen = cross.fZ; return atan2( crossLen, dot ); } //// CreateConvexHull //////////////////////////////////////////////////////// // Algorithm is Graham's scan algorithm: // R.L. Graham, "An efficient algorithm for determining the convex hull of a finite // planar set", Info. Proc. Lett. 1, 132-133 (1972). // Note: THIS WILL DESTROY YOUR INPOINTS ARRAY. static hsBool CreateConvexHull( hsPoint3 *inPoints, int &numPoints ) { int i, j, pointA, pointB, pointC; hsScalar *angles; if( numPoints < 3 ) return false; // Step 1: Find a point interior to our hull. Easiest is average of all our input points... // (plus: set the Zs of all the points to the Z of the first point, since we want to be // working in 2D) hsPoint3 avgPoint = inPoints[ 0 ]; for( i = 1; i < numPoints; i++ ) { avgPoint += inPoints[ i ]; inPoints[ i ].fZ = inPoints[ 0 ].fZ; } avgPoint.fX /= numPoints; avgPoint.fY /= numPoints; avgPoint.fZ /= numPoints; // Step 2: Sort all the in points by the angle to the X axis (vector <1,0>). // Step A: Calculate all the angles angles = TRACKED_NEW hsScalar[ numPoints ]; hsPoint3 xAxisPoint( avgPoint.fX + 1, avgPoint.fY, avgPoint.fZ ); for( i = 0; i < numPoints; i++ ) angles[ i ] = GetVectorAngle( avgPoint, inPoints[ i ], xAxisPoint ); // Step B: Bubble sort by the angles for( i = 0; i < numPoints - 1; i++ ) { for( j = i + 1; j < numPoints; j++ ) { if( angles[ j ] < angles[ i ] ) { hsScalar tempAngle = angles[ j ]; angles[ j ] = angles[ i ]; angles[ i ] = tempAngle; hsPoint3 tempPt = inPoints[ j ]; inPoints[ j ] = inPoints[ i ]; inPoints[ i ] = tempPt; } } } // Step 3: Eliminate non-convex points to form the hull for( pointA = 0, pointB = 1, pointC = 2; pointA < numPoints && numPoints > 3; ) { // Two cases of wrap-around... if( pointC >= numPoints ) pointC -= numPoints; else if( pointC < 0 ) pointC += numPoints; if( pointB >= numPoints ) pointB -= numPoints; else if( pointB < 0 ) pointB += numPoints; // For points A, B, and C, find the interior angle between them hsScalar angle = GetVectorAngle( inPoints[ pointB ], inPoints[ pointA ], inPoints[ pointC ] ); // If the angle is < 180, then it's a good angle and we can advance all our points by 1... // Note: we have a tolerance so that we don't get points that form edges that are pretty darned close... const hsScalar tolerance = hsScalarPI / 90.f; if( angle > tolerance && angle < hsScalarPI - tolerance ) { pointA++; pointB++; pointC++; } else { // Angle is > 180 degrees, this is bad. This means our middle point doesn't belong, // so we need to remove it for( i = pointB; i < numPoints - 1; i++ ) inPoints[ i ] = inPoints[ i + 1 ]; numPoints--; if( pointC > pointB ) pointC--; // There's one case where point B and C could've wrapped around and so deleting that point // actually moves point A down by 1... if( pointA > pointB ) pointA--; // Back up the points by 1 if possible (so we can keep checking to make sure we're still convex). // If not, just increment C up if( pointA > 0 ) { pointA--; pointB--; } else pointC++; } } delete [] angles; return true; } //// GetObjectPoints ///////////////////////////////////////////////////////// // Retrieves ALL of the points of a sceneObject's meshes. And I mean ALL of // 'em... static void GetObjectPoints( plSceneObject *so, hsTArray &outPoints ) { const plDrawInterface* di = so->GetDrawInterface(); if( !di ) return; // The following uses mf's spiffy plAccessGeometry/Spans stuff, which, in // one word, kicksAss. hsTArray spans; plAccessGeometry::Instance()->OpenRO( di, spans ); int i; outPoints.Reset(); for( i = 0; i < spans.GetCount(); i++ ) { plAccessVtxSpan& vtxSrc = spans[ i ].AccessVtx(); plAccPositionIterator iterSrc( &vtxSrc ); for( iterSrc.Begin(); iterSrc.More(); iterSrc.Advance() ) outPoints.Append( *iterSrc.Position() ); } if (plAccessGeometry::Instance()) plAccessGeometry::Instance()->Close( spans ); } //// PointsOnSameSide //////////////////////////////////////////////////////// // Given two ends of a line segment and two points, tells you whether the // two points are on the same side of the line. Used in PointInTriangle(). static hsBool PointsOnSameSide( const hsPoint3 &line1, const hsPoint3 &line2, const hsPoint3 &pointA, const hsPoint3 &pointB ) { hsVector3 baseVec( &line2, &line1 ); hsVector3 cp1 = hsVector3( &pointA, &line1 ) % baseVec; hsVector3 cp2 = hsVector3( &pointB, &line1 ) % baseVec; return ( cp1.fZ * cp2.fZ > 0 ) ? true : false; } //// PointInTriangle ///////////////////////////////////////////////////////// // Given three points that define a triangle and a fourth point, tells you // whether the fourth point is inside the triangle. static hsBool PointInTriangle( hsPoint3 tri1, hsPoint3 tri2, hsPoint3 tri3, const hsPoint3 &testPoint ) { tri1.fZ = tri2.fZ = tri3.fZ = testPoint.fZ; if( PointsOnSameSide( tri1, tri2, testPoint, tri3 ) && PointsOnSameSide( tri2, tri3, testPoint, tri1 ) && PointsOnSameSide( tri3, tri1, testPoint, tri2 ) ) return true; return false; } //// PointInBounds /////////////////////////////////////////////////////////// // Tells you whether said point is in the control's bounds. hsBool pfGUIControlMod::PointInBounds( const hsPoint3 &point ) { UpdateBounds(); if( fBounds.GetType() != kBoundsEmpty && fBounds.GetType() != kBoundsUninitialized && fBounds.IsInside( &point ) ) { if( fBoundsPoints.GetCount() > 0 ) { // We have a more-accurate bounds set, so use it int i; for( i = 1; i < fBoundsPoints.GetCount() - 1; i++ ) { // Test the triangle (0,i,i+1) if( PointInTriangle( fBoundsPoints[ 0 ], fBoundsPoints[ i ], fBoundsPoints[ i + 1 ], point ) ) return true; } return false; } else return true; } return false; } //// CalcInitialBounds /////////////////////////////////////////////////////// // Called by the dialog once as soon as the dialog adds the control, so that // initial bounds for the control can be calced. This is used for initing // any dynmaic text maps, since we want to use the initial bounds to do so // instead of any currently animated state of the bounds. void pfGUIControlMod::CalcInitialBounds( void ) { UpdateBounds( nil, true ); fInitialBounds = fBounds; } //// UpdateBounds //////////////////////////////////////////////////////////// void pfGUIControlMod::UpdateBounds( hsMatrix44 *invXformMatrix, hsBool force ) { hsMatrix44 xformMatrix, projMatrix; hsPoint3 corners[ 8 ]; int i; if( ( !fBoundsValid || force ) && fDialog && GetTarget() ) { plDrawInterface *DI = IGetTargetDrawInterface( 0 ); if( DI == nil ) return; if( HasFlag( kBetterHitTesting ) ) { hsTArray scrnPoints; // Create a list of points to make a 2D convex hull from GetObjectPoints( GetTarget(), scrnPoints ); hsMatrix44 l2w = GetTarget()->GetLocalToWorld(); for( i = 0; i < scrnPoints.GetCount(); i++ ) { scrnPoints[ i ] = l2w * scrnPoints[ i ]; scrnPoints[ i ] = fDialog->WorldToScreenPoint( scrnPoints[ i ] ); } // Now create a convex hull from them, assuming the Zs are all the same int numPoints = scrnPoints.GetCount(); if( !CreateConvexHull( scrnPoints.AcquireArray(), numPoints ) ) return; // Copy & store. Also recalc our bounding box just for fun fBounds.MakeEmpty(); fBoundsPoints.SetCount( numPoints ); for( i = 0; i < numPoints; i++ ) { fBoundsPoints[ i ] = scrnPoints[ i ]; fBounds.Union( &fBoundsPoints[ i ] ); } } else { fBounds.MakeEmpty(); hsBounds3Ext worldBounds = DI->GetLocalBounds(); hsMatrix44 l2w = GetTarget()->GetLocalToWorld(); worldBounds.Transform( &l2w ); worldBounds.GetCorners( corners ); for( i = 0; i < 8; i++ ) { hsPoint3 scrnPt = fDialog->WorldToScreenPoint( corners[ i ] ); fBounds.Union( &scrnPt ); } } // Calc center Z // if( !fCenterValid ) { #if 0 corners[ 1 ] = GetTarget()->GetLocalToWorld().GetTranslate(); float w = corners[ 1 ].fX * fXformMatrix.fMap[3][0] + corners[ 1 ].fY * fXformMatrix.fMap[3][1] + corners[ 1 ].fZ * fXformMatrix.fMap[3][2] + 1.f * fXformMatrix.fMap[3][3]; corners[ 1 ] = fXformMatrix * corners[ 1 ]; corners[ 1 ].fX = ( ( corners[ 1 ].fX / corners[ 1 ].fZ ) + 1.f ) / 2.f; corners[ 1 ].fY = ( ( corners[ 1 ].fY / corners[ 1 ].fZ ) + 1.f ) / 2.f; fScreenCenter = corners[ 1 ]; // fScreenCenter.fZ = w; corners[ 1 ] = GetTarget()->GetLocalToWorld().GetTranslate(); fDialog->WorldToScreenPoint( corners[ 1 ].fX, corners[ 1 ].fY, corners[ 1 ].fZ, fScreenCenter ); fCenterValid = true; #else corners[ 1 ] = GetTarget()->GetLocalToWorld().GetTranslate(); fScreenCenter = fDialog->WorldToScreenPoint( corners[ 1 ] ); corners[ 1 ] = fScreenCenter; fCenterValid = true; #endif } fScreenMinZ = fBounds.GetMins().fZ; // Manually change the bounds so we know the z ranges from at least -1 to 1, suitable for us testing against for clicks corners[ 0 ] = fBounds.GetCenter(); corners[ 0 ].fZ = -1.f; fBounds.Union( &corners[ 0 ] ); corners[ 0 ].fZ = 1.f; fBounds.Union( &corners[ 0 ] ); fBoundsValid = true; } } //// SetObjectCenter ///////////////////////////////////////////////////////// // Given the x/y coordinates in 0..1 space, recalcs the sceneObject position // and moves the object to match, retaining the stored fCenterZ coordinate void pfGUIControlMod::SetObjectCenter( hsScalar x, hsScalar y ) { hsMatrix44 xformMatrix, l2p, p2l; hsPoint3 center, corners[ 8 ]; if( x > 1.f ) x = 1.f; else if( x < 0.f ) x = 0.f; if( y > 1.f ) y = 1.f; else if( y < 0.f ) y = 0.f; if( fDialog && GetTarget() ) { plCoordinateInterface *CI = IGetTargetCoordinateInterface( 0 ); if( CI == nil ) return; // if( !fInvXformValid ) // UpdateBounds(); l2p = GetTarget()->GetLocalToWorld(); hsPoint3 oldPt = l2p.GetTranslate(); hsPoint3 oldScrnPt = fDialog->WorldToScreenPoint( oldPt ); hsPoint3 oldPtRedux; fDialog->ScreenToWorldPoint( oldScrnPt.fX, oldScrnPt.fY, oldScrnPt.fZ, oldPtRedux ); fDialog->ScreenToWorldPoint( x, y, fScreenCenter.fZ, center ); l2p.SetTranslate( ¢er ); l2p.GetInverse( &p2l ); GetTarget()->SetTransform( l2p, p2l ); fScreenCenter.fX = x; fScreenCenter.fY = y; } } void pfGUIControlMod::SetTarget( plSceneObject *object ) { plSingleModifier::SetTarget( object ); UpdateBounds(); } //// MsgReceive ////////////////////////////////////////////////////////////// #include "plProfile.h" plProfile_CreateTimer("Gui", "RenderSetup", GUITime); hsBool pfGUIControlMod::MsgReceive( plMessage *msg ) { plRenderMsg* rend = plRenderMsg::ConvertNoRef( msg ); if( rend ) { plProfile_BeginLap(GUITime, this->GetKey()->GetUoid().GetObjectName()); // Only need it once if( ISetUpDynTextMap( rend->Pipeline() ) ) plgDispatch::Dispatch()->UnRegisterForExactType( plRenderMsg::Index(), GetKey() ); plProfile_EndLap(GUITime, this->GetKey()->GetUoid().GetObjectName()); return true; } plGenRefMsg *refMsg = plGenRefMsg::ConvertNoRef( msg ); if( refMsg != nil ) { if( refMsg->fType == kRefDynTextMap ) { if( refMsg->GetContext() & ( plRefMsg::kOnCreate | plRefMsg::kOnRequest | plRefMsg::kOnReplace ) ) { fDynTextMap = plDynamicTextMap::ConvertNoRef( refMsg->GetRef() ); // Register for a render msg so we can leech the material when we finally // have a pipeline to work with plgDispatch::Dispatch()->RegisterForExactType( plRenderMsg::Index(), GetKey() ); } else fDynTextMap = nil; return true; } else if( refMsg->fType == kRefDynTextLayer ) { if( refMsg->GetContext() & ( plRefMsg::kOnCreate | plRefMsg::kOnRequest | plRefMsg::kOnReplace ) ) fDynTextLayer = plLayerInterface::ConvertNoRef( refMsg->GetRef() ); else fDynTextLayer = nil; return true; } else if( refMsg->fType == kRefProxy ) { if( refMsg->GetContext() & ( plRefMsg::kOnCreate | plRefMsg::kOnRequest | plRefMsg::kOnReplace ) ) fProxy = plSceneObject::ConvertNoRef( refMsg->GetRef() ); else fProxy = nil; return true; } else if( refMsg->fType == kRefSkin ) { if( refMsg->GetContext() & ( plRefMsg::kOnCreate | plRefMsg::kOnRequest | plRefMsg::kOnReplace ) ) fSkin = pfGUISkin::ConvertNoRef( refMsg->GetRef() ); else fSkin = nil; return true; } } return plSingleModifier::MsgReceive( msg ); } //// ISetUpDynTextMap //////////////////////////////////////////////////////// // Given a pointer to a dynamic text map, regurgitates it so it matches our // screen res and fun stuff like that. Also sets the layer transform to give // us a 1:1 textel-pixel ratio, which we like. hsBool pfGUIControlMod::ISetUpDynTextMap( plPipeline *pipe ) { if( fDynTextMap == nil ) { hsAssert( false, "Trying to set up a nil dynamicTextMap in a GUI control" ); return true; } if( fDynTextLayer == nil || fInitialBounds.GetType() == kBoundsUninitialized )//|| fDialog == nil ) return false; UInt32 scrnWidth, scrnHeight; if( !HasFlag( kScaleTextWithResolution ) ) { // Scale so that there is a 1:1 pixel:textel ratio scrnWidth = pipe->Width(); scrnHeight = pipe->Height(); } else { // Scale with the resolution so that we take up the same % of screen space no matter what resolution // Assume a base "resolution" of 1024xX, where X is such that the ratio "1024/X = scrnWidth/scrnHt" holds const int kBaseScaleRes = 1024; const int kBaseScaleHeightRes = 768; scrnWidth = kBaseScaleRes; scrnHeight = kBaseScaleHeightRes; // we are going to just force things to be in 4 by 3 ratio... // ...cause it seems to work better. /////// scrnHeight = ( pipe->Height() * kBaseScaleRes ) / pipe->Width(); } const hsBounds3 &bounds = fInitialBounds;//GetBounds(); UInt16 width = (UInt16)(( bounds.GetMaxs().fX - bounds.GetMins().fX ) * scrnWidth); UInt16 height = (UInt16)(( bounds.GetMaxs().fY - bounds.GetMins().fY ) * scrnHeight); // Allow derived controls to allocate some extra scratch space if desired // (Do it this way so we can pass in our current calculated dimensions for them to play with) UInt16 extraW = width, extraH = height; IGrowDTMDimsToDesiredSize( extraW, extraH ); extraW -= width; extraH -= height; fDynTextMap->Reset(); fDynTextMap->Create( width, height, HasFlag( kXparentBgnd ), extraW, extraH ); fDynTextMap->SetFont( GetColorScheme()->fFontFace, GetColorScheme()->fFontSize, GetColorScheme()->fFontFlags, HasFlag( kXparentBgnd ) ? false : true ); fDynTextMap->SetTextColor( GetColorScheme()->fForeColor, ( HasFlag( kXparentBgnd ) && GetColorScheme()->fBackColor.a == 0.f ) ? true : false ); // Now we gotta set the texture transform on the layer so our texture comes // out with 1:1 mapping from textel to pixel plLayer *layer = (plLayer *)fDynTextLayer; layer->SetTransform( fDynTextMap->GetLayerTransform() ); // Let the derived classes do their things IPostSetUpDynTextMap(); // Do our first update IUpdate(); return true; } //// Get/SetColorScheme ////////////////////////////////////////////////////// pfGUIColorScheme *pfGUIControlMod::GetColorScheme( void ) const { if( fColorScheme == nil ) return fDialog->GetColorScheme(); return fColorScheme; } void pfGUIControlMod::SetColorScheme( pfGUIColorScheme *newScheme ) { if( fColorScheme != nil ) { hsRefCnt_SafeUnRef( fColorScheme ); fColorScheme = nil; } fColorScheme = newScheme; if( fColorScheme != nil ) hsRefCnt_SafeRef( fColorScheme ); } //// SetDynTextMap /////////////////////////////////////////////////////////// // EXPORT ONLY void pfGUIControlMod::SetDynTextMap( plLayerInterface *layer, plDynamicTextMap *dynText ) { hsgResMgr::ResMgr()->AddViaNotify( layer->GetKey(), TRACKED_NEW plGenRefMsg( GetKey(), plRefMsg::kOnCreate, -1, pfGUIControlMod::kRefDynTextLayer ), plRefFlags::kActiveRef ); hsgResMgr::ResMgr()->AddViaNotify( dynText->GetKey(), TRACKED_NEW plGenRefMsg( GetKey(), plRefMsg::kOnCreate, -1, pfGUIControlMod::kRefDynTextMap ), plRefFlags::kActiveRef ); } //// SetEnabled ////////////////////////////////////////////////////////////// void pfGUIControlMod::SetEnabled( hsBool e ) { if( e == fEnabled ) return; fEnabled = e; IUpdate(); } //// SetFocused ////////////////////////////////////////////////////////////// void pfGUIControlMod::SetFocused( hsBool e ) { if( e == fFocused ) return; fFocused = e; IUpdate(); } //// SetInteresting ////////////////////////////////////////////////////////// void pfGUIControlMod::SetInteresting( hsBool i ) { if( i == fInteresting ) return; fInteresting = i; IUpdate(); if ( fNotifyOnInteresting && fDialog && fDialog->GetHandler() ) fDialog->GetHandler()->OnInterestingEvent(this); } //// SetVisible ////////////////////////////////////////////////////////////// void pfGUIControlMod::SetVisible( hsBool vis ) { if( vis == fVisible ) return; fVisible = vis; if (fTarget) { plEnableMsg *msg = TRACKED_NEW plEnableMsg(); msg->SetCmd( fVisible ? plEnableMsg::kEnable : plEnableMsg::kDisable ); msg->SetCmd( plEnableMsg::kDrawable ); msg->AddReceiver( fTarget->GetKey() ); plgDispatch::MsgSend( msg ); } if( !fVisible && fFocused ) fDialog->SetFocus( nil ); } void pfGUIControlMod::Refresh( void ) { IUpdate(); } //// Read/Write ////////////////////////////////////////////////////////////// void pfGUIControlMod::Read( hsStream *s, hsResMgr *mgr ) { plSingleModifier::Read(s, mgr); s->ReadSwap( &fTagID ); fVisible = s->ReadBool(); // Read the handler in ISetHandler( pfGUICtrlProcWriteableObject::Read( s ) ); // Read in the dynTextMap if there is one if( s->ReadBool() ) { mgr->ReadKeyNotifyMe( s, TRACKED_NEW plGenRefMsg( GetKey(), plRefMsg::kOnCreate, -1, kRefDynTextLayer ), plRefFlags::kActiveRef ); mgr->ReadKeyNotifyMe( s, TRACKED_NEW plGenRefMsg( GetKey(), plRefMsg::kOnCreate, -1, kRefDynTextMap ), plRefFlags::kActiveRef ); } else { fDynTextLayer = nil; fDynTextMap = nil; } if( s->ReadBool() ) { SetColorScheme( nil ); fColorScheme = TRACKED_NEW pfGUIColorScheme(); fColorScheme->Read( s ); } // Read in our sound indices UInt8 i, count = s->ReadByte(); if( count == 0 ) fSoundIndices.Reset(); else { fSoundIndices.SetCountAndZero( count ); for( i = 0; i < count; i++ ) fSoundIndices[ i ] = (int)s->ReadSwap32(); } if( HasFlag( kHasProxy ) ) mgr->ReadKeyNotifyMe( s, TRACKED_NEW plGenRefMsg( GetKey(), plRefMsg::kOnCreate, -1, kRefProxy ), plRefFlags::kActiveRef ); mgr->ReadKeyNotifyMe( s, TRACKED_NEW plGenRefMsg( GetKey(), plRefMsg::kOnCreate, -1, kRefSkin ), plRefFlags::kActiveRef ); } void pfGUIControlMod::Write( hsStream *s, hsResMgr *mgr ) { if( HasFlag( kHasProxy ) && !fProxy ) ClearFlag( kHasProxy ); plSingleModifier::Write( s, mgr ); s->WriteSwap( fTagID ); s->WriteBool( fVisible ); // Write the handler out (if it's not a writeable, damn you) pfGUICtrlProcWriteableObject::Write( (pfGUICtrlProcWriteableObject *)fHandler, s ); // Write out the dynTextMap if( fDynTextMap != nil ) { s->WriteBool( true ); mgr->WriteKey( s, fDynTextLayer->GetKey() ); mgr->WriteKey( s, fDynTextMap->GetKey() ); } else s->WriteBool( false ); if( fColorScheme != nil ) { s->WriteBool( true ); fColorScheme->Write( s ); } else s->WriteBool( false ); // Write out our sound indices s->WriteByte( fSoundIndices.GetCount() ); UInt8 i; for( i = 0; i < fSoundIndices.GetCount(); i++ ) s->WriteSwap32( fSoundIndices[ i ] ); if( HasFlag( kHasProxy ) ) mgr->WriteKey( s, fProxy->GetKey() ); mgr->WriteKey( s, fSkin ); } //// HandleKeyPress/Event //////////////////////////////////////////////////// hsBool pfGUIControlMod::HandleKeyPress( char key, UInt8 modifiers ) { return false; } hsBool pfGUIControlMod::HandleKeyEvent( pfGameGUIMgr::EventType event, plKeyDef key, UInt8 modifiers ) { return false; } //// IScreenToLocalPt //////////////////////////////////////////////////////// void pfGUIControlMod::IScreenToLocalPt( hsPoint3 &pt ) { const hsBounds3 &bnds = GetBounds(); pt.fX -= bnds.GetMins().fX; pt.fY -= bnds.GetMins().fY; pt.fX /= bnds.GetMaxs().fX - bnds.GetMins().fX; pt.fY /= bnds.GetMaxs().fY - bnds.GetMins().fY; } //// ISetHandler ///////////////////////////////////////////////////////////// void pfGUIControlMod::ISetHandler( pfGUICtrlProcObject *h, hsBool clearInheritFlag ) { if( fHandler && fHandler->DecRef() ) delete fHandler; fHandler = h; if( fHandler ) fHandler->IncRef(); if( clearInheritFlag ) ClearFlag( kInheritProcFromDlg ); } //// DoSomething ///////////////////////////////////////////////////////////// void pfGUIControlMod::DoSomething( void ) { if( fEnabled && fHandler != nil ) fHandler->DoSomething( this ); } //// HandleExtendedEvent ///////////////////////////////////////////////////// void pfGUIControlMod::HandleExtendedEvent( UInt32 event ) { if( fEnabled && fHandler != nil ) fHandler->HandleExtendedEvent( this, event ); } //// SetDropTargetHdlr /////////////////////////////////////////////////////// void pfGUIControlMod::SetDropTargetHdlr( pfGUIDropTargetProc *h ) { if( fDropTargetHdlr && fDropTargetHdlr->DecRef() ) delete fDropTargetHdlr; fDropTargetHdlr = h; if( fDropTargetHdlr ) fDropTargetHdlr->IncRef(); } //// SetSoundIndex /////////////////////////////////////////////////////////// // Associates the given GUI event with an index of a sound on the target SO's // audioInterface. The guiCtrlEvent is specific to each type of control. void pfGUIControlMod::SetSoundIndex( UInt8 guiCtrlEvent, int soundIndex ) { if( fSoundIndices.GetCount() < guiCtrlEvent + 1 ) fSoundIndices.ExpandAndZero( guiCtrlEvent + 1 ); fSoundIndices[ guiCtrlEvent ] = soundIndex + 1; // We +1, since 0 means no sound } //// IPlaySound ////////////////////////////////////////////////////////////// // Sends a sound play message with the soundIndex associated with the given // event. void pfGUIControlMod::IPlaySound( UInt8 guiCtrlEvent, hsBool loop /* = false */ ) { if( guiCtrlEvent >= fSoundIndices.GetCount() || fSoundIndices[ guiCtrlEvent ] == 0 ) return; if( GetTarget() == nil || GetTarget()->GetAudioInterface() == nil ) return; plSoundMsg *msg = TRACKED_NEW plSoundMsg; msg->fIndex = fSoundIndices[ guiCtrlEvent ] - 1; msg->SetCmd( plSoundMsg::kGoToTime ); msg->fTime = 0.f; msg->SetCmd( plSoundMsg::kPlay ); if (loop) { msg->fLoop = true; msg->SetCmd( plSoundMsg::kSetLooping ); } msg->Send( GetTarget()->GetAudioInterface()->GetKey() ); } void pfGUIControlMod::IStopSound(UInt8 guiCtrlEvent) { if (guiCtrlEvent >= fSoundIndices.GetCount() || fSoundIndices[guiCtrlEvent] == 0) return; if (GetTarget() == nil || GetTarget()->GetAudioInterface() == nil ) return; plSoundMsg *msg = TRACKED_NEW plSoundMsg; msg->fIndex = fSoundIndices[guiCtrlEvent] - 1; msg->SetCmd(plSoundMsg::kStop); msg->Send(GetTarget()->GetAudioInterface()->GetKey()); }