/*==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==*/ ////////////////////////////////////////////////////////////////////////////// // // // pfGUIPopUpMenu Header // // // // Pop-up menus are really just dialogs that know how to create themselves // // and create buttons on themselves to simulate a menu (after all, that's // // all a menu really is anyway). // // // ////////////////////////////////////////////////////////////////////////////// #include "hsTypes.h" #include "pfGameGUIMgr.h" #include "pfGUIPopUpMenu.h" #include "pfGUIMenuItem.h" #include "pfGUIButtonMod.h" #include "pfGUIDialogHandlers.h" #include "pfGUIDialogNotifyProc.h" #include "pfGUIControlHandlers.h" #include "pfGUICtrlGenerator.h" #include "plgDispatch.h" #include "hsResMgr.h" #include "../plSurface/hsGMaterial.h" #include "../plSurface/plLayer.h" #include "../plGImage/plDynamicTextMap.h" #include "../plMessage/plLayRefMsg.h" #include "../pnSceneObject/plSceneObject.h" #include "../pnSceneObject/plDrawInterface.h" #include "../pnSceneObject/plCoordinateInterface.h" #include "../pnMessage/plIntRefMsg.h" #include "../pnMessage/plObjRefMsg.h" #include "../pnMessage/plNodeRefMsg.h" #include "../plScene/plPostEffectMod.h" #include "../plScene/plSceneNode.h" #include "../pnMessage/plClientMsg.h" #include "plViewTransform.h" #include "../plPipeline/plDebugText.h" class pfPopUpKeyGenerator { public: char fPrefix[ 128 ]; UInt32 fKeyCount; plLocation fLoc; pfPopUpKeyGenerator( const char *p, const plLocation &loc ) { strcpy( fPrefix, p ); fLoc = loc; } plKey CreateKey( hsKeyedObject *ko ) { char name[ 256 ]; sprintf( name, "%s-%d", fPrefix, fKeyCount++ ); return hsgResMgr::ResMgr()->NewKey( name, ko, fLoc ); } }; //// Router Proc So The Parent Can Handle Click Events /////////////////////// class pfGUIMenuItemProc : public pfGUICtrlProcObject { protected: pfGUIPopUpMenu *fParent; UInt32 fIndex; public: pfGUIMenuItemProc( pfGUIPopUpMenu *parent, UInt32 idx ) { fParent = parent; fIndex = idx; } virtual void DoSomething( pfGUIControlMod *ctrl ) { fParent->IHandleMenuSomething( fIndex, ctrl ); } virtual void HandleExtendedEvent( pfGUIControlMod *ctrl, UInt32 event ) { fParent->IHandleMenuSomething( fIndex, ctrl, (Int32)event ); } }; //// Constructor/Destructor ////////////////////////////////////////////////// pfGUIPopUpMenu::pfGUIPopUpMenu() { fNeedsRebuilding = false; fParent = nil; fKeyGen = nil; fSubMenuOpen = -1; SetFlag( kModalOutsideMenus ); fMargin = 4; fSkin = nil; fWaitingForSkin = false; fParentNode = nil; fOriginX = fOriginY = 0.f; fOriginAnchor = nil; fOriginContext = nil; fAlignment = kAlignDownRight; } pfGUIPopUpMenu::~pfGUIPopUpMenu() { SetSkin( nil ); // if( fParentNode != nil ) // fParentNode->GetKey()->UnRefObject(); ITearDownMenu(); ClearItems(); delete fKeyGen; } //// MsgReceive ////////////////////////////////////////////////////////////// hsBool pfGUIPopUpMenu::MsgReceive( plMessage *msg ) { plGenRefMsg *ref = plGenRefMsg::ConvertNoRef( msg ); if( ref != nil ) { if( ref->fType == kRefSkin ) { if( ref->GetContext() & ( plRefMsg::kOnCreate | plRefMsg::kOnRequest | plRefMsg::kOnReplace ) ) { fSkin = pfGUISkin::ConvertNoRef( ref->GetRef() ); fWaitingForSkin = false; } else fSkin = nil; fNeedsRebuilding = true; if( IsVisible() ) { // Rebuild NOW ITearDownMenu(); IBuildMenu(); } return true; } else if( ref->fType == kRefSubMenu ) { if( ref->GetContext() & ( plRefMsg::kOnCreate | plRefMsg::kOnRequest | plRefMsg::kOnReplace ) ) fMenuItems[ ref->fWhich ].fSubMenu = pfGUIPopUpMenu::ConvertNoRef( ref->GetRef() ); else fMenuItems[ ref->fWhich ].fSubMenu = nil; return true; } else if( ref->fType == kRefOriginAnchor ) { if( ref->GetContext() & ( plRefMsg::kOnCreate | plRefMsg::kOnRequest | plRefMsg::kOnReplace ) ) fOriginAnchor = plSceneObject::ConvertNoRef( ref->GetRef() ); else fOriginAnchor = nil; return true; } else if( ref->fType == kRefOriginContext ) { if( ref->GetContext() & ( plRefMsg::kOnCreate | plRefMsg::kOnRequest | plRefMsg::kOnReplace ) ) fOriginContext = pfGUIDialogMod::ConvertNoRef( ref->GetRef() ); else fOriginContext = nil; return true; } else if( ref->fType == kRefParentNode ) { if( ref->GetContext() & ( plRefMsg::kOnCreate | plRefMsg::kOnRequest | plRefMsg::kOnReplace ) ) fParentNode = plSceneNode::ConvertNoRef( ref->GetRef() ); else fParentNode = nil; return true; } } return pfGUIDialogMod::MsgReceive( msg ); } //// Read/Write ////////////////////////////////////////////////////////////// void pfGUIPopUpMenu::Read( hsStream *s, hsResMgr *mgr ) { pfGUIDialogMod::Read( s, mgr ); // In case we need it... fKeyGen = TRACKED_NEW pfPopUpKeyGenerator( GetName(), GetKey()->GetUoid().GetLocation() ); fOriginX = fOriginY = -1.f; fMargin = s->ReadSwap16(); UInt32 i, count = s->ReadSwap32(); fMenuItems.SetCountAndZero( count ); for( i = 0; i < count; i++ ) { char readTemp[ 256 ]; s->Read( sizeof( readTemp ), readTemp ); wchar_t *wReadTemp = hsStringToWString( readTemp ); fMenuItems[ i ].fName = wReadTemp; delete [] wReadTemp; fMenuItems[ i ].fHandler = pfGUICtrlProcWriteableObject::Read( s ); mgr->ReadKeyNotifyMe( s, TRACKED_NEW plGenRefMsg( GetKey(), plRefMsg::kOnCreate, i, kRefSubMenu ), plRefFlags::kActiveRef ); } mgr->ReadKeyNotifyMe( s, TRACKED_NEW plGenRefMsg( GetKey(), plRefMsg::kOnCreate, -1, kRefSkin ), plRefFlags::kActiveRef ); mgr->ReadKeyNotifyMe( s, TRACKED_NEW plGenRefMsg( GetKey(), plRefMsg::kOnCreate, -1, kRefOriginAnchor ), plRefFlags::kPassiveRef ); mgr->ReadKeyNotifyMe( s, TRACKED_NEW plGenRefMsg( GetKey(), plRefMsg::kOnCreate, -1, kRefOriginContext ), plRefFlags::kPassiveRef ); fAlignment = (Alignment)s->ReadByte(); fNeedsRebuilding = true; } void pfGUIPopUpMenu::Write( hsStream *s, hsResMgr *mgr ) { pfGUIDialogMod::Write( s, mgr ); s->WriteSwap16( fMargin ); s->WriteSwap32( fMenuItems.GetCount() ); UInt32 i; for( i = 0; i < fMenuItems.GetCount(); i++ ) { char writeTemp[ 256 ]; char *sName = hsWStringToString( fMenuItems[ i ].fName.c_str() ); strncpy( writeTemp, sName, sizeof( writeTemp ) ); delete [] sName; s->Write( sizeof( writeTemp ), writeTemp ); // Write the handler out (if it's not a writeable, damn you) pfGUICtrlProcWriteableObject::Write( (pfGUICtrlProcWriteableObject *)fMenuItems[ i ].fHandler, s ); mgr->WriteKey( s, fMenuItems[ i ].fSubMenu ); } // Note: we force parentNode to nil here because we only use it when we dynamically // create nodes at runtime and need to unref and destroy them later. Since we're // reading from disk, we'll already have a sceneNode somewhere, so we don't need // this. fParentNode = nil; mgr->WriteKey( s, fSkin ); mgr->WriteKey( s, fOriginAnchor ); mgr->WriteKey( s, fOriginContext ); s->WriteByte( (UInt8)fAlignment ); } void pfGUIPopUpMenu::SetOriginAnchor( plSceneObject *anchor, pfGUIDialogMod *context ) { fOriginAnchor = anchor; fOriginContext = context; hsgResMgr::ResMgr()->AddViaNotify( fOriginAnchor->GetKey(), TRACKED_NEW plGenRefMsg( GetKey(), plRefMsg::kOnCreate, -1, kRefOriginAnchor ), plRefFlags::kPassiveRef ); hsgResMgr::ResMgr()->AddViaNotify( fOriginContext->GetKey(), TRACKED_NEW plGenRefMsg( GetKey(), plRefMsg::kOnCreate, -1, kRefOriginContext ), plRefFlags::kPassiveRef ); } //// SetEnabled ////////////////////////////////////////////////////////////// void pfGUIPopUpMenu::SetEnabled( hsBool e ) { if( e && fNeedsRebuilding ) { // Make sure our menu is rebuilt before enabling ITearDownMenu(); IBuildMenu(); } else if( !e ) { if( fParent != nil ) pfGUIPopUpMenu::ConvertNoRef( fParent )->fSubMenuOpen = -1; // Hide our submenus if we have any open if( fSubMenuOpen != -1 ) { fMenuItems[ fSubMenuOpen ].fSubMenu->Hide(); fSubMenuOpen = -1; } } pfGUIDialogMod::SetEnabled( e ); } void pfGUIPopUpMenu::Show( hsScalar x, hsScalar y ) { fOriginX = x; fOriginY = y; pfGUIDialogMod::Show(); // C++ is kinda stupid if it can't find this naturally ISeekToOrigin(); } void pfGUIPopUpMenu::ISeekToOrigin( void ) { #if 0 UInt32 i; float x = 0.5f/*fOriginX*/, y = fOriginY; for( i = 0; i < fControls.GetCount(); i++ ) { if( fControls[ i ] != nil ) { fControls[ i ]->SetObjectCenter( x, y ); // const hsBounds3 &bnds = fControls[ i ]->GetBounds(); y += fMenuItems[ i ].fYOffsetToNext;//bnds.GetMaxs().fY - bnds.GetMins().fY; /* hsMatrix44 p2l, l2p = GetTarget()->GetLocalToWorld(); hsPoint3 center, origin; ScreenToWorldPoint( fOriginX, fOriginY, 100.f, center ); ScreenToWorldPoint( 0.f, 0.f, 100.f, origin ); center = origin - center; center.fZ = 0.f; l2p.SetTranslate( ¢er ); l2p.GetInverse( &p2l ); GetTarget()->SetTransform( l2p, p2l ); */ } } #endif } //// IHandleMenuSomething //////////////////////////////////////////////////// // Handles a normal event from one of the item controls. void pfGUIPopUpMenu::IHandleMenuSomething( UInt32 idx, pfGUIControlMod *ctrl, Int32 extended ) { if( extended != -1 ) { if( fSubMenuOpen != -1 && fSubMenuOpen != idx ) { // Better close the submenu(s) fMenuItems[ fSubMenuOpen ].fSubMenu->Hide(); fSubMenuOpen = -1; } if( extended == pfGUIMenuItem::kMouseHover && fMenuItems[ idx ].fSubMenu != nil ) { // Open new submenu const hsBounds3 &bnds = ctrl->GetBounds(); fMenuItems[ idx ].fSubMenu->Show( bnds.GetMaxs().fX, bnds.GetMins().fY ); fSubMenuOpen = idx; } } else { if( fMenuItems[ idx ].fHandler != nil ) fMenuItems[ idx ].fHandler->DoSomething( ctrl ); // If item isn't a sub-menu, close this menu. Else add to list of menus to close // once the smallest submenu goes away if( fMenuItems[ idx ].fSubMenu == nil ) { // Basically, we want to hide ourselves and as many up in the chain of command as // can be hidden pfGUIPopUpMenu *menu = this; while( menu != nil && !menu->HasFlag( kStayOpenAfterClick ) ) { menu->Hide(); menu = pfGUIPopUpMenu::ConvertNoRef( menu->fParent ); } } else { // Show relative to the corner of our current item const hsBounds3 &bnds = ctrl->GetBounds(); fMenuItems[ idx ].fSubMenu->Show( bnds.GetMaxs().fX, bnds.GetMins().fY ); fSubMenuOpen = idx; } } } //// IBuildMenu ////////////////////////////////////////////////////////////// // Given the list of menu items, builds our set of dynamic buttons hsBool pfGUIPopUpMenu::IBuildMenu( void ) { int i; if( fWaitingForSkin && fSkin == nil ) return false; // Still waiting to get our skin before building pfGUIColorScheme *scheme = TRACKED_NEW pfGUIColorScheme(); scheme->fForeColor.Set( 0, 0, 0, 1 ); scheme->fBackColor.Set( 1, 1, 1, 1 ); // If we don't have origin points, get them from translating our anchor if( fOriginX == -1 || fOriginY == -1 && fOriginAnchor != nil ) { hsPoint3 scrnPt; const plDrawInterface *di = fOriginAnchor->GetDrawInterface(); if( di != nil ) { scrnPt = di->GetLocalBounds().GetCenter(); scrnPt = fOriginAnchor->GetLocalToWorld() * scrnPt; } else scrnPt = fOriginAnchor->GetLocalToWorld().GetTranslate(); if( fOriginContext != nil ) scrnPt = fOriginContext->WorldToScreenPoint( scrnPt ); else scrnPt = WorldToScreenPoint( scrnPt ); if( fOriginX == -1 ) fOriginX = scrnPt.fX; if( fOriginY == -1 ) fOriginY = scrnPt.fY; } float x = fOriginX, y = fOriginY; float width = 0.f, height = 0.f; float topMargin = ( fSkin != nil ) ? fSkin->GetBorderMargin() : 0.f; // First step: loop through and calculate the size of our menu // The PROBLEM is that we can't do that unless we have a friggin surface on // which to calculate the text extents! So sadly, we're going to have to create // a whole new DTMap and use it to calculate some stuff plDynamicTextMap *scratch = TRACKED_NEW plDynamicTextMap( 8, 8, false ); scratch->SetFont( scheme->fFontFace, scheme->fFontSize, scheme->fFontFlags, true ); for( i = 0; i < fMenuItems.GetCount(); i++ ) { UInt16 thisW, thisH; thisW = scratch->CalcStringWidth( fMenuItems[ i ].fName.c_str(), &thisH ); if( fMenuItems[ i ].fSubMenu != nil ) { if( fSkin != nil ) thisW += 4 + ( fSkin->GetElement( pfGUISkin::kSubMenuArrow ).fWidth << 1 ); else thisW += scratch->CalcStringWidth( " >>", nil ); } thisH += 2; // Give us at least one pixel on each side int margin = fMargin; if( fSkin != nil ) margin = fSkin->GetBorderMargin() << 1; if( width < thisW + margin ) width = (float)(thisW + margin); if( fSkin != nil ) margin = fSkin->GetItemMargin() << 1; if( height < thisH + margin ) height = (float)(thisH + margin); } delete scratch; width += 4; // give us a little space, just in case UInt32 scrnWidth, scrnHeight; // A cheat here, I know, but I'm lazy plDebugText::Instance().GetScreenSize( &scrnWidth, &scrnHeight ); // Use the same base res calc that dtMaps use if( !HasFlag( kScaleWithResolution ) ) { // Just use what we were passed in } 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; scrnHeight = ( scrnHeight * kBaseScaleRes ) / scrnWidth; scrnWidth = kBaseScaleRes; } width /= (float)scrnWidth; height /= (float)scrnHeight; topMargin /= (float)scrnHeight; switch( fAlignment ) { case kAlignUpLeft: x -= width; y -= height * fMenuItems.GetCount(); break; case kAlignUpRight: y -= height * fMenuItems.GetCount(); break; case kAlignDownLeft: x -= width; break; case kAlignDownRight: break; } if( y + height * fMenuItems.GetCount() > 1.f ) { // Make sure we don't go off the bottom y = 1.f - height * fMenuItems.GetCount(); } // And the top (takes precedence) if( y < 0.f ) y = 0.f; // Control positions are in the lower left corner, so increment Y by 1 control height first y += height;// + topMargin; hsTArray buildList; for( i = 0; i < fMenuItems.GetCount(); i++ ) { hsGMaterial *mat = ICreateDynMaterial(); float thisMargin = ( i == 0 || i == fMenuItems.GetCount() - 1 ) ? topMargin : 0.f; float thisOffset = ( i == fMenuItems.GetCount() - 1 ) ? topMargin : 0.f; pfGUIMenuItem *button = pfGUIMenuItem::ConvertNoRef( pfGUICtrlGenerator::Instance().CreateRectButton( this, fMenuItems[ i ].fName.c_str(), x, y + thisOffset, width, height + thisMargin, mat, true ) ); if( button != nil ) { button->SetColorScheme( scheme ); button->SetName( fMenuItems[ i ].fName.c_str() ); button->SetHandler( TRACKED_NEW pfGUIMenuItemProc( this, i ) ); // make the tag ID the position in the menu list button->SetTagID(i); button->SetDynTextMap( mat->GetLayer( 0 ), plDynamicTextMap::ConvertNoRef( mat->GetLayer( 0 )->GetTexture() ) ); button->SetFlag( pfGUIMenuItem::kReportHovers ); button->SetSkin( fSkin, ( i == 0 ) ? pfGUIMenuItem::kTop : ( i == fMenuItems.GetCount() - 1 ) ? pfGUIMenuItem::kBottom : pfGUIMenuItem::kMiddle ); if( fMenuItems[ i ].fSubMenu != nil ) { button->SetFlag( pfGUIMenuItem::kDrawSubMenuArrow ); buildList.Append( pfGUIPopUpMenu::ConvertNoRef( fMenuItems[ i ].fSubMenu ) ); } } // Tiny bit of overlap to prevent gaps fMenuItems[ i ].fYOffsetToNext = height + thisOffset; y += height + thisOffset;// - ( 1.f / kBaseScaleResY ); } fNeedsRebuilding = false; #if 0 // Finally, go down our list of submenus and rebuild them, since they'll need to be rebuilt soon anyway, // and at least this way it's all in one pass // Also, we need to bump the tag ID used, such as adding parent menuItem TagID * 100.. or something // Disabled because right now we can't move menus, which is required for this to work for( i = 0; i < buildList.GetCount(); i++ ) buildList[ i ]->IBuildMenu(); #endif return true; } //// ITearDownMenu /////////////////////////////////////////////////////////// // Destroys all of our dynamic controls representing the menu void pfGUIPopUpMenu::ITearDownMenu( void ) { int i; for( i = 0; i < fControls.GetCount(); i++ ) { if( fControls[ i ] != nil ) { // It's not enough to release the key, we have to have the sceneNode release the key, too. // Easy enough to do by just setting it's sn to nil if( fControls[ i ]->GetTarget() != nil ) fControls[ i ]->GetTarget()->SetSceneNode( nil ); // Now release it from us GetKey()->Release( fControls[ i ]->GetKey() ); } } fNeedsRebuilding = true; } //// HandleMouseEvent //////////////////////////////////////////////////////// hsBool pfGUIPopUpMenu::HandleMouseEvent( pfGameGUIMgr::EventType event, hsScalar mouseX, hsScalar mouseY, UInt8 modifiers ) { hsBool r = pfGUIDialogMod::HandleMouseEvent( event, mouseX, mouseY, modifiers ); if( r == false && event == pfGameGUIMgr::kMouseUp ) { // We don't want to be active anymore! if( !HasFlag( kStayOpenAfterClick ) ) { Hide(); // Now we pass the click to our parent. Why? Because it's possible that someone above us // will either a) also want to hide (cancel the entire menu selection) or b) select // another option if( fParent != nil ) return fParent->HandleMouseEvent( event, mouseX, mouseY, modifiers ); } } return ( fParent != nil ) ? r : ( HasFlag( kModalOutsideMenus ) || ( fSubMenuOpen != -1 ) ); } //// ClearItems ////////////////////////////////////////////////////////////// // Clears the list of template items void pfGUIPopUpMenu::ClearItems( void ) { int i; for( i = 0; i < fMenuItems.GetCount(); i++ ) { if( fMenuItems[ i ].fHandler != nil ) { if( fMenuItems[ i ].fHandler->DecRef() ) delete fMenuItems[ i ].fHandler; } } fMenuItems.Reset(); fNeedsRebuilding = true; } //// AddItem ///////////////////////////////////////////////////////////////// // Append a new item to the list of things to build the menu from void pfGUIPopUpMenu::AddItem( const char *name, pfGUICtrlProcObject *handler, pfGUIPopUpMenu *subMenu ) { wchar_t *wName = hsStringToWString(name); AddItem(wName,handler,subMenu); delete [] wName; } void pfGUIPopUpMenu::AddItem( const wchar_t *name, pfGUICtrlProcObject *handler, pfGUIPopUpMenu *subMenu ) { pfMenuItem newItem; newItem.fName = name; newItem.fHandler = handler; if( newItem.fHandler != nil ) newItem.fHandler->IncRef(); newItem.fSubMenu = subMenu; if( subMenu != nil ) subMenu->fParent = this; fMenuItems.Append( newItem ); fNeedsRebuilding = true; } //// ICreateDynMaterial ////////////////////////////////////////////////////// // Creates the hsGMaterial tree for a single layer with a plDynamicTextMap. hsGMaterial *pfGUIPopUpMenu::ICreateDynMaterial( void ) { hsColorRGBA black, white; // Create the new dynTextMap plDynamicTextMap *textMap = TRACKED_NEW plDynamicTextMap(); fKeyGen->CreateKey( textMap ); // Create the material hsGMaterial *material = TRACKED_NEW hsGMaterial; fKeyGen->CreateKey( material ); // Create the layer and attach plLayer *lay = material->MakeBaseLayer(); white.Set( 1.f,1.f,1.f,1.f ); black.Set( 0.f,0.f,0.f,1.f ); lay->SetRuntimeColor( black ); lay->SetPreshadeColor( black ); lay->SetAmbientColor( white ); lay->SetClampFlags( hsGMatState::kClampTexture ); // Do sendRef here, since we're going to need it set pretty darned quick hsgResMgr::ResMgr()->SendRef( textMap->GetKey(), TRACKED_NEW plLayRefMsg( lay->GetKey(), plRefMsg::kOnCreate, 0, plLayRefMsg::kTexture ), plRefFlags::kActiveRef ); return material; } //// Build /////////////////////////////////////////////////////////////////// // Constructs a shiny new pop-up menu at runtime, complete with trimmings #include "../plJPEG/plJPEG.h" pfGUIPopUpMenu *pfGUIPopUpMenu::Build( const char *name, pfGUIDialogMod *parent, hsScalar x, hsScalar y, const plLocation &destLoc ) { float fovX, fovY; // Create the menu and give it a key gen pfGUIPopUpMenu *menu = TRACKED_NEW pfGUIPopUpMenu(); menu->fKeyGen = TRACKED_NEW pfPopUpKeyGenerator( name, destLoc ); menu->fKeyGen->CreateKey( menu ); menu->fOriginX = x; menu->fOriginY = y; // By default, share the same skin as the parent if( parent != nil && ( (pfGUIPopUpMenu *)parent )->fSkin != nil ) { menu->fWaitingForSkin = true; hsgResMgr::ResMgr()->SendRef( ( (pfGUIPopUpMenu *)parent )->fSkin->GetKey(), TRACKED_NEW plGenRefMsg( menu->GetKey(), plRefMsg::kOnCreate, -1, pfGUIPopUpMenu::kRefSkin ), plRefFlags::kActiveRef ); } // HACK for now: create us a temp skin to use /* static pfGUISkin *skin = nil; if( skin == nil ) { plLocation loc; loc.Set( 0x1425 ); plKey skinKey = hsgResMgr::ResMgr()->FindKey( plUoid( loc, pfGUISkin::Index(), "GUISkin01_GUISkin" ) ); menu->fWaitingForSkin = true; hsgResMgr::ResMgr()->AddViaNotify( skinKey, TRACKED_NEW plGenRefMsg( menu->GetKey(), plRefMsg::kOnCreate, -1, pfGUIPopUpMenu::kRefSkin ), plRefFlags::kActiveRef ); } */ // Create the rendermod plPostEffectMod *renderMod = TRACKED_NEW plPostEffectMod; menu->fKeyGen->CreateKey( renderMod ); renderMod->SetHither( 0.5f ); renderMod->SetYon( 200.f ); float scrnWidth = 20.f; // fovX should be such that scrnWidth is the projected width at z=100 fovX = atan( scrnWidth / ( 2.f * 100.f ) ) * 2.f; fovY = fovX;// * 3.f / 4.f; renderMod->SetFovX( fovX * 180.f / hsScalarPI ); renderMod->SetFovY( fovY * 180.f / hsScalarPI ); // Create the sceneNode to go with it menu->fParentNode= TRACKED_NEW plSceneNode; menu->fKeyGen->CreateKey( menu->fParentNode ); // menu->fParentNode->GetKey()->RefObject(); hsgResMgr::ResMgr()->SendRef( menu->fParentNode->GetKey(), TRACKED_NEW plGenRefMsg( menu->GetKey(), plRefMsg::kOnCreate, 0, kRefParentNode ), plRefFlags::kActiveRef ); hsgResMgr::ResMgr()->AddViaNotify( menu->fParentNode->GetKey(), TRACKED_NEW plGenRefMsg( renderMod->GetKey(), plRefMsg::kOnCreate, 0, plPostEffectMod::kNodeRef ), plRefFlags::kPassiveRef ); menu->SetRenderMod( renderMod ); menu->SetName( name ); // Create the dummy scene object to hold the menu plSceneObject *newObj = TRACKED_NEW plSceneObject; menu->fKeyGen->CreateKey( newObj ); // *#&$(*@&#$ need a coordIface... plCoordinateInterface *newCI = TRACKED_NEW plCoordinateInterface; menu->fKeyGen->CreateKey( newCI ); hsMatrix44 l2w, w2l; l2w.Reset(); l2w.GetInverse( &w2l ); // Using SendRef here because AddViaNotify will queue the messages up, which doesn't do us any good // if we need these refs right away hsgResMgr::ResMgr()->SendRef( newCI->GetKey(), TRACKED_NEW plObjRefMsg( newObj->GetKey(), plRefMsg::kOnCreate, 0, plObjRefMsg::kInterface ), plRefFlags::kActiveRef ); hsgResMgr::ResMgr()->SendRef( renderMod->GetKey(), TRACKED_NEW plObjRefMsg( newObj->GetKey(), plRefMsg::kOnCreate, 0, plObjRefMsg::kModifier ), plRefFlags::kActiveRef ); newObj->SetSceneNode( menu->fParentNode->GetKey() ); newObj->SetTransform( l2w, w2l ); hsgResMgr::ResMgr()->SendRef( menu->GetKey(), TRACKED_NEW plObjRefMsg( newObj->GetKey(), plRefMsg::kOnCreate, 0, plObjRefMsg::kModifier ), plRefFlags::kActiveRef ); // Add the menu to the GUI mgr plGenRefMsg *refMsg = TRACKED_NEW plGenRefMsg( pfGameGUIMgr::GetInstance()->GetKey(), plRefMsg::kOnCreate, 0, pfGameGUIMgr::kDlgModRef ); hsgResMgr::ResMgr()->AddViaNotify( menu->GetKey(), refMsg, plRefFlags::kActiveRef ); menu->ISeekToOrigin(); return menu; } //// SetSkin ///////////////////////////////////////////////////////////////// void pfGUIPopUpMenu::SetSkin( pfGUISkin *skin ) { // Just a function wrapper for SendRef if( fSkin != nil ) GetKey()->Release( fSkin->GetKey() ); if( skin != nil ) { hsgResMgr::ResMgr()->SendRef( skin->GetKey(), TRACKED_NEW plGenRefMsg( GetKey(), plRefMsg::kOnCreate, -1, kRefSkin ), plRefFlags::kActiveRef ); fWaitingForSkin = true; } else fWaitingForSkin = false; } ////////////////////////////////////////////////////////////////////////////// //// pfGUISkin Implementation //////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// pfGUISkin::pfGUISkin() { fTexture = nil; memset( fElements, 0, sizeof( pfSRect ) * kNumElements ); } pfGUISkin::pfGUISkin( plMipmap *texture ) { fTexture = texture; if( fTexture != nil ) { hsAssert( fTexture->GetKey() != nil, "Creating a GUI skin via a mipmap with no key!" ); fTexture->GetKey()->RefObject(); } memset( fElements, 0, sizeof( pfSRect ) * kNumElements ); } pfGUISkin::~pfGUISkin() { SetTexture( nil ); } void pfGUISkin::SetTexture( plMipmap *tex ) { if( fTexture != nil && fTexture->GetKey() != nil ) fTexture->GetKey()->UnRefObject(); fTexture = tex; if( fTexture != nil ) { hsAssert( fTexture->GetKey() != nil, "Creating a GUI skin via a mipmap with no key!" ); fTexture->GetKey()->RefObject(); } } void pfGUISkin::SetElement( UInt32 idx, UInt16 x, UInt16 y, UInt16 w, UInt16 h ) { fElements[ idx ].fX = x; fElements[ idx ].fY = y; fElements[ idx ].fWidth = w; fElements[ idx ].fHeight = h; } void pfGUISkin::Read( hsStream *s, hsResMgr *mgr ) { hsKeyedObject::Read( s, mgr ); s->ReadSwap( &fItemMargin ); s->ReadSwap( &fBorderMargin ); UInt32 i, count; s->ReadSwap( &count ); for( i = 0; i < count; i++ ) fElements[ i ].Read( s ); for( ; i < kNumElements; i++ ) fElements[ i ].Empty(); mgr->ReadKeyNotifyMe( s, TRACKED_NEW plGenRefMsg( GetKey(), plRefMsg::kOnCreate, -1, kRefMipmap ), plRefFlags::kActiveRef ); } void pfGUISkin::Write( hsStream *s, hsResMgr *mgr ) { hsKeyedObject::Write( s, mgr ); s->WriteSwap( fItemMargin ); s->WriteSwap( fBorderMargin ); UInt32 i = kNumElements; s->WriteSwap( i ); for( i = 0; i < kNumElements; i++ ) fElements[ i ].Write( s ); mgr->WriteKey( s, fTexture ); } hsBool pfGUISkin::MsgReceive( plMessage *msg ) { plGenRefMsg *ref = plGenRefMsg::ConvertNoRef( msg ); if( ref != nil ) { if( ref->GetContext() & ( plRefMsg::kOnCreate | plRefMsg::kOnRequest | plRefMsg::kOnReplace ) ) fTexture = plMipmap::ConvertNoRef( ref->GetRef() ); else fTexture = nil; return true; } return hsKeyedObject::MsgReceive( msg ); } void pfGUISkin::pfSRect::Read( hsStream *s ) { s->ReadSwap( &fX ); s->ReadSwap( &fY ); s->ReadSwap( &fWidth ); s->ReadSwap( &fHeight ); } void pfGUISkin::pfSRect::Write( hsStream *s ) { s->WriteSwap( fX ); s->WriteSwap( fY ); s->WriteSwap( fWidth ); s->WriteSwap( fHeight ); }