/*==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 . Additional permissions under GNU GPL version 3 section 7 If you modify this Program, or any covered work, by linking or combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK, NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK (or a modified version of those libraries), containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA, PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the licensors of this Program grant you additional permission to convey the resulting work. Corresponding Source for a non-source form of such a combination shall include the source code for the parts of OpenSSL and IJG JPEG Library used as well as that of the covered work. 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==*/ ////////////////////////////////////////////////////////////////////////////// // // // pfGUIDialogMod Definition // // // ////////////////////////////////////////////////////////////////////////////// #include "hsTypes.h" #include "pfGameGUIMgr.h" #include "pfGUIDialogMod.h" #include "pfGUIControlMod.h" #include "pfGUIDialogHandlers.h" #include "pfGUIDialogNotifyProc.h" #include "pfGUIListElement.h" #include "../plScene/plPostEffectMod.h" #include "../pnMessage/plRefMsg.h" #include "../pfMessage/pfGameGUIMsg.h" #include "../plMessage/plAnimCmdMsg.h" #include "../plScene/plSceneNode.h" #include "../pnSceneObject/plSceneObject.h" #include "../pnKeyedObject/plKey.h" #include "../pnKeyedObject/plFixedKey.h" #include "../pnSceneObject/plCoordinateInterface.h" #include "../plStatusLog/plStatusLog.h" #include "plgDispatch.h" #include "hsResMgr.h" #include "plViewTransform.h" //// Constructor/Destructor ////////////////////////////////////////////////// pfGUIDialogMod::pfGUIDialogMod() : fRenderMod( nil ), fNext( nil ), fPrevPtr( nil ) { memset( fName, 0, sizeof( fName ) ); fEnabled = false; fControlOfInterest = nil; fFocusCtrl = nil; fMousedCtrl = nil; fTagID = 0; fHandler = nil; fVersion = 0; fDragMode = false; fDragReceptive = false; fDragTarget = nil; fProcReceiver = nil; fColorScheme = TRACKED_NEW pfGUIColorScheme(); } pfGUIDialogMod::~pfGUIDialogMod() { // Call the handler's destroy if there is one if( fHandler ) fHandler->OnDestroy(); // Unregister us with the Game GUI manager plUoid lu( kGameGUIMgr_KEY ); plKey mgrKey = hsgResMgr::ResMgr()->FindKey( lu ); if( mgrKey ) { plGenRefMsg *refMsg = TRACKED_NEW plGenRefMsg( mgrKey, plRefMsg::kOnRemove, 0, pfGameGUIMgr::kDlgModRef ); refMsg->SetRef( this ); plgDispatch::MsgSend( refMsg ); } SetHandler( nil ); hsRefCnt_SafeUnRef( fColorScheme ); fColorScheme = nil; } //// ScreenToWorldPoint ////////////////////////////////////////////////////// // Sometimes it just sucks not having access to the pipeline at just the // right time. void pfGUIDialogMod::ScreenToWorldPoint( hsScalar x, hsScalar y, hsScalar z, hsPoint3 &outPt ) { plViewTransform view = fRenderMod->GetViewTransform(); view.SetScreenSize( 1, 1 ); outPt = view.ScreenToWorld( hsPoint3( x, y, z ) ); } //// WorldToScreenPoint ////////////////////////////////////////////////////// // Given a point in world-space, translates it into screen coordinates // (with range 0-1, origin top-left). hsPoint3 pfGUIDialogMod::WorldToScreenPoint( const hsPoint3 &inPt ) { plViewTransform view = fRenderMod->GetViewTransform(); view.SetScreenSize( 1, 1 ); hsPoint3 tempPt = view.WorldToScreen( inPt ); tempPt.fZ = view.WorldToCamera( inPt ).fZ; return tempPt; } //// IEval /////////////////////////////////////////////////////////////////// hsBool pfGUIDialogMod::IEval( double secs, hsScalar del, UInt32 dirty ) { return false; } //// MsgReceive ////////////////////////////////////////////////////////////// hsBool pfGUIDialogMod::MsgReceive( plMessage *msg ) { plGenRefMsg *ref = plGenRefMsg::ConvertNoRef( msg ); if( ref ) { switch( ref->fType ) { case kRenderModRef: if( ref->GetContext() & ( plRefMsg::kOnCreate | plRefMsg::kOnRequest | plRefMsg::kOnReplace ) ) { fRenderMod = plPostEffectMod::ConvertNoRef( ref->GetRef() ); fRenderMod->EnableLightsOnRenderRequest(); if( fEnabled ) { plAnimCmdMsg *animMsg = TRACKED_NEW plAnimCmdMsg( GetKey(), fRenderMod->GetKey(), nil ); animMsg->SetCmd( plAnimCmdMsg::kContinue ); plgDispatch::MsgSend( animMsg ); } } else if( ref->GetContext() & ( plRefMsg::kOnRemove | plRefMsg::kOnDestroy ) ) { plAnimCmdMsg *animMsg = TRACKED_NEW plAnimCmdMsg( GetKey(), fRenderMod->GetKey(), nil ); animMsg->SetCmd( plAnimCmdMsg::kStop ); plgDispatch::MsgSend( animMsg ); fRenderMod = nil; } break; case kControlRef: if( ref->GetContext() & ( plRefMsg::kOnCreate | plRefMsg::kOnRequest | plRefMsg::kOnReplace ) ) { if( ref->fWhich >= fControls.GetCount() ) { hsAssert( false, "Bad index in reffing a control on a GUI dialog" ); } else { pfGUIControlMod *oldCtrl = fControls[ ref->fWhich ]; fControls[ ref->fWhich ] = pfGUIControlMod::ConvertNoRef( ref->GetRef() ); fControls[ ref->fWhich ]->ISetDialog( this ); if( oldCtrl != fControls[ ref->fWhich ] ) // They're equal on export time, when we DON'T want to be updating the bounds fControls[ ref->fWhich ]->CalcInitialBounds(); if( fControls[ ref->fWhich ]->HasFlag( pfGUIControlMod::kInheritProcFromDlg ) ) fControls[ ref->fWhich ]->ISetHandler( fHandler ); } } else if( ref->GetContext() & ( plRefMsg::kOnRemove | plRefMsg::kOnDestroy ) ) { if( ref->fWhich >= fControls.GetCount() ) { hsAssert( false, "Bad index in unreffing a control on a GUI dialog." ); } else { if( fControls[ ref->fWhich ] != nil ) fControls[ ref->fWhich ]->ISetDialog( nil ); fControls[ ref->fWhich ] = nil; } } break; } return true; } return plSingleModifier::MsgReceive( msg ); } //// AddControl ////////////////////////////////////////////////////////////// void pfGUIDialogMod::AddControl( pfGUIControlMod *ctrl ) { fControls.Append( ctrl ); ctrl->ISetDialog( this ); ctrl->CalcInitialBounds(); } //// AddControlOnExport ////////////////////////////////////////////////////// void pfGUIDialogMod::AddControlOnExport( pfGUIControlMod *ctrl ) { fControls.Append( ctrl ); hsgResMgr::ResMgr()->AddViaNotify( ctrl->GetKey(), TRACKED_NEW plGenRefMsg( GetKey(), plRefMsg::kOnCreate, fControls.GetCount() - 1, pfGUIDialogMod::kControlRef ), plRefFlags::kActiveRef ); } //// SetEnabled ////////////////////////////////////////////////////////////// void pfGUIDialogMod::SetEnabled( hsBool e ) { if( e == fEnabled ) return; fEnabled = e; if( fHandler != nil ) { if( fEnabled ) fHandler->OnShow(); else fHandler->OnHide(); } if ( !fEnabled ) { // if we are being hidden then there should be no controls that have interest fControlOfInterest = nil; // also we can purge the dynaText images on the controls int i; for( i = 0; i < fControls.GetCount(); i++ ) { if( fControls[ i ] == nil ) continue; fControls[ i ]->PurgeDynaTextMapImage(); } } if( fRenderMod != nil ) { plAnimCmdMsg *animMsg = TRACKED_NEW plAnimCmdMsg( GetKey(), fRenderMod->GetKey(), nil ); if( fEnabled ) { animMsg->SetCmd( plAnimCmdMsg::kContinue ); // Update the bounds on all controls that we own UpdateAllBounds(); } else animMsg->SetCmd( plAnimCmdMsg::kStop ); plgDispatch::MsgSend( animMsg ); } } //// Read/Write ////////////////////////////////////////////////////////////// void pfGUIDialogMod::Read( hsStream *s, hsResMgr *mgr ) { plSingleModifier::Read(s, mgr); mgr->ReadKeyNotifyMe( s, TRACKED_NEW plGenRefMsg( GetKey(), plRefMsg::kOnCreate, -1, kRenderModRef ), plRefFlags::kActiveRef ); s->Read( sizeof( fName ), fName ); UInt32 i, count = s->ReadSwap32(); fControls.SetCountAndZero( count ); for( i = 0; i < count; i++ ) mgr->ReadKeyNotifyMe( s, TRACKED_NEW plGenRefMsg( GetKey(), plRefMsg::kOnCreate, i, kControlRef ), plRefFlags::kActiveRef ); // Register us with the Game GUI manager plUoid lu( kGameGUIMgr_KEY ); plKey mgrKey = hsgResMgr::ResMgr()->FindKey( lu ); if( mgrKey ) { plGenRefMsg *refMsg = TRACKED_NEW plGenRefMsg( mgrKey, plRefMsg::kOnCreate, 0, pfGameGUIMgr::kDlgModRef ); hsgResMgr::ResMgr()->AddViaNotify( GetKey(), refMsg, plRefFlags::kPassiveRef ); } s->ReadSwap( &fTagID ); fProcReceiver = mgr->ReadKey( s ); if( fProcReceiver != nil ) SetHandler( TRACKED_NEW pfGUIDialogNotifyProc( fProcReceiver ) ); s->ReadSwap( &fVersion ); fColorScheme->Read( s ); fSceneNodeKey = mgr->ReadKey( s ); } void pfGUIDialogMod::Write( hsStream *s, hsResMgr *mgr ) { UInt32 i; plSingleModifier::Write( s, mgr ); mgr->WriteKey( s, fRenderMod->GetKey() ); s->Write( sizeof( fName ), fName ); s->WriteSwap32( fControls.GetCount() ); for( i = 0; i < fControls.GetCount(); i++ ) mgr->WriteKey( s, fControls[ i ]->GetKey() ); s->WriteSwap( fTagID ); mgr->WriteKey( s, fProcReceiver ); s->WriteSwap( fVersion ); fColorScheme->Write( s ); mgr->WriteKey( s, fSceneNodeKey ); } plKey pfGUIDialogMod::GetSceneNodeKey( void ) { if( fSceneNodeKey != nil ) return fSceneNodeKey; // Attempt to grab it if( GetTarget() != nil && GetTarget()->GetSceneNode() != nil ) return ( fSceneNodeKey = GetTarget()->GetSceneNode() ); return nil; } //// UpdateInterestingThings ///////////////////////////////////////////////// // Really. We go through and make sure every control marked as interesting // still has the mouse inside it and vice versa. void pfGUIDialogMod::UpdateInterestingThings( hsScalar mouseX, hsScalar mouseY, UInt8 modifiers, hsBool modalPreset ) { int i; hsPoint3 mousePoint; mousePoint.Set( mouseX, mouseY, 0.f ); for( i = 0; i < fControls.GetCount(); i++ ) { if( fControls[ i ] == nil ) continue; // if there was a modal present and we are not modal, then everything is unInteresting! if ( modalPreset && !HasFlag(pfGUIDialogMod::kModal) ) { if( fControls[ i ]->IsInteresting() ) fControls[ i ]->SetInteresting( false ); } else { if( !fControls[ i ]->HasFlag( pfGUIControlMod::kIntangible ) && fControls[ i ]->PointInBounds( mousePoint ) || fControls[ i ] == fControlOfInterest ) { if( !fControls[ i ]->IsInteresting() ) fControls[ i ]->SetInteresting( true ); } else { if( fControls[ i ]->IsInteresting() ) fControls[ i ]->SetInteresting( false ); } } } } //// HandleMouseEvent //////////////////////////////////////////////////////// #ifdef HS_DEBUGGING // Debugging bounds rects #include "../plPipeline/plDebugText.h" #endif hsBool pfGUIDialogMod::HandleMouseEvent( pfGameGUIMgr::EventType event, hsScalar mouseX, hsScalar mouseY, UInt8 modifiers ) { hsPoint3 mousePoint; UInt32 i; pfGUIControlMod *oldInterestingCtrl = nil; hsScalar smallestZ; #ifdef HS_DEBUGGING // Debugging bounds rects static bool showBounds = false; if( showBounds ) { UInt32 sW, sH; plDebugText::Instance().GetScreenSize(&sW,&sH); for( i = 0; i < fControls.GetCount(); i++ ) { if( fControls[ i ] == nil ) continue; if( fControls[ i ]->HasFlag( pfGUIControlMod::kIntangible ) ) continue; if( fControls[ i ]->fBoundsPoints.GetCount() > 0 ) { const hsBounds3 &bnds = fControls[ i ]->GetBounds(); plDebugText::Instance().Draw3DBorder( (UInt16)(sW * bnds.GetMins().fX), (UInt16)(sH * bnds.GetMins().fY), (UInt16)(sW * bnds.GetMaxs().fX), (UInt16)(sH * bnds.GetMaxs().fY), 0x3000ffff, 0x3000ffff ); UInt32 color = 0xffff0000; for( int j = 0; j < fControls[ i ]->fBoundsPoints.GetCount(); j++ ) { // color = 0xff000000 | ( ( j * 16 ) << 16 ); float x = sW * fControls[ i ]->fBoundsPoints[ j ].fX; float y = sH * fControls[ i ]->fBoundsPoints[ j ].fY; plDebugText::Instance().DrawRect( (UInt16)(x - 2), (UInt16)(y - 2), (UInt16)(x + 2), (UInt16)(y + 2), color ); char str[ 16 ]; itoa( j, str, 10 ); plDebugText::Instance().DrawString( (UInt16)(x + 8), (UInt16)(y - 8), str, color ); } } else { const hsBounds3 &bnds = fControls[ i ]->GetBounds(); plDebugText::Instance().Draw3DBorder( (UInt16)(sW * bnds.GetMins().fX), (UInt16)(sH * bnds.GetMins().fY), (UInt16)(sW * bnds.GetMaxs().fX), (UInt16)(sH * bnds.GetMaxs().fY), 0x300000ff, 0x300000ff ); } } } #endif mousePoint.Set( mouseX, mouseY, 0.f ); if( fDragMode ) { IHandleDrag( mousePoint, event, modifiers ); return true; // We ALWAYS handle events if we're in drag mode } oldInterestingCtrl = fMousedCtrl; if( fControlOfInterest != nil ) { // A particular control already has interest--pass messages directly to it no matter what fMousedCtrl = fControlOfInterest; } else { for( i = 0, fMousedCtrl = nil, smallestZ = 1.e30f; i < fControls.GetCount(); i++ ) { if( fControls[ i ] != nil && !fControls[ i ]->HasFlag( pfGUIControlMod::kIntangible ) && fControls[ i ]->PointInBounds( mousePoint ) && fControls[ i ]->IsVisible() && fControls[ i ]->IsEnabled() ) { if( fControls[ i ]->GetScreenMinZ() < smallestZ ) { if( fControls[ i ]->FilterMousePosition( mousePoint ) ) { fMousedCtrl = fControls[ i ]; smallestZ = fControls[ i ]->GetScreenMinZ(); } } } } } if( fMousedCtrl != nil ) { #ifdef HS_DEBUGGING // Debugging bounds rects if( showBounds ) { const hsBounds3 &bnds = fMousedCtrl->GetBounds(); plDebugText::Instance().DrawString( (UInt16)(bnds.GetMins().fX), (UInt16)(bnds.GetMins().fY), fMousedCtrl->GetKeyName(), (UInt32)0xffffff00 ); } #endif if( event == pfGameGUIMgr::kMouseDown ) { if( fMousedCtrl->HasFlag( pfGUIControlMod::kWantsInterest ) ) fControlOfInterest = fMousedCtrl; fMousedCtrl->HandleMouseDown( mousePoint, modifiers ); // Clicking on a control (mouse down) also sets focus to that control. Unlike // control-of-interest, this does NOT get reset until a new control is clicked on if( fFocusCtrl != fMousedCtrl ) { if( fHandler != nil ) fHandler->OnCtrlFocusChange( fFocusCtrl, fMousedCtrl ); if( fFocusCtrl != nil ) fFocusCtrl->SetFocused( false ); fFocusCtrl = fMousedCtrl; fFocusCtrl->SetFocused( true ); } } else if( event == pfGameGUIMgr::kMouseUp ) { fMousedCtrl->HandleMouseUp( mousePoint, modifiers ); // Controls lose interest on mouse up fControlOfInterest = nil; } else if( event == pfGameGUIMgr::kMouseMove ) fMousedCtrl->HandleMouseHover( mousePoint, modifiers ); else if( event == pfGameGUIMgr::kMouseDrag ) fMousedCtrl->HandleMouseDrag( mousePoint, modifiers ); else if( event == pfGameGUIMgr::kMouseDblClick ) fMousedCtrl->HandleMouseDblClick( mousePoint, modifiers ); return true; } // Clicked on nobody, make sure we lose focus on any controls if( fFocusCtrl != nil && event == pfGameGUIMgr::kMouseDown ) { if( fHandler != nil ) fHandler->OnCtrlFocusChange( fFocusCtrl, nil ); if( fFocusCtrl != nil ) // The handler call could've changed it fFocusCtrl->SetFocused( false ); fFocusCtrl = nil; } return false; } //// HandleKeyEvent ////////////////////////////////////////////////////////// hsBool pfGUIDialogMod::HandleKeyEvent( pfGameGUIMgr::EventType event, plKeyDef key, UInt8 modifiers ) { // Only process if a control has focus... if( fFocusCtrl != nil ) { // And guess what, it's up to that control to process it! Gee, how easy... return fFocusCtrl->HandleKeyEvent( event, key, modifiers ); } return false; } //// HandleKeyPress ////////////////////////////////////////////////////////// hsBool pfGUIDialogMod::HandleKeyPress( char key, UInt8 modifiers ) { // Same deal as HandleKeyPress. Only problem is, we needed the msg to translate // to a char, so it had to be done up at the mgr level (sadly) // Only process if a control has focus... if( fFocusCtrl != nil ) { return fFocusCtrl->HandleKeyPress( key, modifiers ); } return false; } //// SetFocus //////////////////////////////////////////////////////////////// void pfGUIDialogMod::SetFocus( pfGUIControlMod *ctrl ) { if( ctrl != nil && ctrl->fDialog != this ) { if( fHandler != nil ) fHandler->OnCtrlFocusChange( fFocusCtrl, nil ); if( fFocusCtrl != nil ) fFocusCtrl->SetFocused( false ); fFocusCtrl = nil; ctrl->fDialog->SetFocus( ctrl ); } else if( ctrl != fFocusCtrl ) { if( fFocusCtrl != nil ) fFocusCtrl->SetFocused( false ); if( fHandler != nil ) fHandler->OnCtrlFocusChange( fFocusCtrl, ctrl ); fFocusCtrl = ctrl; if( fFocusCtrl != nil ) fFocusCtrl->SetFocused( true ); } } //// Show/Hide /////////////////////////////////////////////////////////////// void pfGUIDialogMod::Show( void ) { pfGameGUIMgr::GetInstance()->ShowDialog( this ); } void pfGUIDialogMod::ShowNoReset( void ) { pfGameGUIMgr::GetInstance()->ShowDialog( this, false ); } void pfGUIDialogMod::Hide( void ) { pfGameGUIMgr::GetInstance()->HideDialog( this ); } //// GetControlFromTag /////////////////////////////////////////////////////// pfGUIControlMod *pfGUIDialogMod::GetControlFromTag( UInt32 tagID ) { int i; int ctrlCount = fControls.GetCount(); for( i = 0; i < ctrlCount; i++ ) { pfGUIControlMod *ctrl = fControls[i]; if( ctrl && ctrl->GetTagID() == tagID ) return fControls[ i ]; } return nil; } //// SetControlOfInterest //////////////////////////////////////////////////// void pfGUIDialogMod::SetControlOfInterest( pfGUIControlMod *c ) { fControlOfInterest = c; } //// SetHandler ////////////////////////////////////////////////////////////// void pfGUIDialogMod::SetHandler( pfGUIDialogProc *hdlr ) { int i; if( fHandler && fHandler->DecRef() ) delete fHandler; fHandler = hdlr; if( fHandler != nil ) { fHandler->IncRef(); fHandler->SetDialog( this ); } // We also set the handler for any controls that are flagged to inherit // from the parent dialog. Note that SetHandlerForAll() can thus be // seen as a function that forces this flag (temporarily) on all controls for( i = 0; i < fControls.GetCount(); i++ ) { // Test for nil controls since we get this also on destruct if( fControls[ i ] != nil && fControls[ i ]->HasFlag( pfGUIControlMod::kInheritProcFromDlg ) ) fControls[ i ]->ISetHandler( hdlr ); } } //// SetHandlerForAll //////////////////////////////////////////////////////// // Does SetHandler() for the dialog and all of its controls. Handy if you // have one of those all-encompasing dialog procs. :) void pfGUIDialogMod::SetHandlerForAll( pfGUIDialogProc *hdlr ) { int i; SetHandler( hdlr ); for( i = 0; i < fControls.GetCount(); i++ ) fControls[ i ]->ISetHandler( hdlr ); } //// SetControlHandler /////////////////////////////////////////////////////// void pfGUIDialogMod::SetControlHandler( UInt32 tagID, pfGUIDialogProc *hdlr ) { int i; for( i = 0; i < fControls.GetCount(); i++ ) { if( fControls[ i ]->GetTagID() == tagID ) { fControls[ i ]->SetHandler( hdlr ); break; } } } //// UpdateAspectRatio /////////////////////////////////////////////////////// void pfGUIDialogMod::UpdateAspectRatio( void ) { if (fRenderMod) { // Set width fov respecting height fov fRenderMod->SetFovX(pfGameGUIMgr::GetInstance()->GetAspectRatio() * fRenderMod->GetFovY()); } UpdateAllBounds(); } //// UpdateAllBounds ///////////////////////////////////////////////////////// void pfGUIDialogMod::UpdateAllBounds( void ) { int i; for( i = 0; i < fControls.GetCount(); i++ ) { if( fControls[ i ] != nil ) fControls[ i ]->UpdateBounds( nil, true ); } } //// RefreshAllControls ////////////////////////////////////////////////////// void pfGUIDialogMod::RefreshAllControls( void ) { int i; for( i = 0; i < fControls.GetCount(); i++ ) fControls[ i ]->IUpdate(); } ////////////////////////////////////////////////////////////////////////////// //// ListElement Drag Functions ////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// //// ClearDragList /////////////////////////////////////////////////////////// void pfGUIDialogMod::ClearDragList( void ) { fDragElements.Reset(); } //// AddToDragList /////////////////////////////////////////////////////////// void pfGUIDialogMod::AddToDragList( pfGUIListElement *e ) { fDragElements.Append( e ); } //// EnterDragMode /////////////////////////////////////////////////////////// void pfGUIDialogMod::EnterDragMode( pfGUIControlMod *source ) { if( fDragElements.GetCount() > 0 ) { fDragMode = true; fDragReceptive = false; fDragTarget = nil; fDragSource = source; } } //// IHandleDrag ///////////////////////////////////////////////////////////// // Oooh, we're in dragging-list-elements-around mode! So completely ignore // the normal way we do things; what we need to do is wait until the mouse // button is up, all the while testing to see if the control we're on top of // is capable of receiving the elements we have. Once the mouse button is let // up, if the control is indeed receptive, we call its drag handler for each // of our elements, and either way, exit drag mode. void pfGUIDialogMod::IHandleDrag( hsPoint3 &mousePoint, pfGameGUIMgr::EventType event, UInt8 modifiers ) { int i; hsScalar smallestZ; // First, see if our target control has changed for( i = 0, fMousedCtrl = nil, smallestZ = 1.e30f; i < fControls.GetCount(); i++ ) { if( fControls[ i ]->PointInBounds( mousePoint ) && fControls[ i ]->GetBounds().GetMaxs().fZ < smallestZ ) fMousedCtrl = fControls[ i ]; } if( fMousedCtrl != fDragTarget ) { // Target has changed, update our receptive flag fDragTarget = fMousedCtrl; if( fDragTarget == nil ) fDragReceptive = false; else { pfGUIDropTargetProc *dropProc = fDragTarget->GetDropTargetHdlr(); if( dropProc == nil ) fDragReceptive = false; else { fDragReceptive = true; for( i = 0; i < fDragElements.GetCount(); i++ ) { if( !dropProc->CanEat( fDragElements[ i ], fDragSource ) ) { fDragReceptive = false; break; } } } } } if( event == pfGameGUIMgr::kMouseUp ) { /// Mouse got let up--we're exiting drag mode, but can we process the drop? fDragMode = false; if( fDragReceptive ) { pfGUIDropTargetProc *dropProc = fDragTarget->GetDropTargetHdlr(); for( i = 0; i < fDragElements.GetCount(); i++ ) dropProc->Eat( fDragElements[ i ], fDragSource, fDragTarget ); } } } //// GetDesiredCursor //////////////////////////////////////////////////////// UInt32 pfGUIDialogMod::GetDesiredCursor( void ) const { if( fMousedCtrl != nil ) return fMousedCtrl->IGetDesiredCursor(); return 0; }